summaryrefslogtreecommitdiffstats
path: root/test/static-code
diff options
context:
space:
mode:
Diffstat (limited to 'test/static-code')
-rwxr-xr-xtest/static-code200
1 files changed, 200 insertions, 0 deletions
diff --git a/test/static-code b/test/static-code
new file mode 100755
index 0000000..4734a7c
--- /dev/null
+++ b/test/static-code
@@ -0,0 +1,200 @@
+#!/bin/bash
+# run static code checks like eslint, flake8, mypy, ruff, vulture.
+
+set -eu
+
+# requires: .flake8
+# requires: pyproject.toml
+# requires: containers/flatpak/test/ruff.toml
+# requires: pkg/ruff.toml
+# requires: test/common/ruff.toml
+# requires: test/example/ruff.toml
+# requires: test/verify/ruff.toml
+# requires: tools/vulture-suppressions/ruff.toml
+
+# we consider any function named test_* to be a test case
+# each test is considered to succeed if it exits with no output
+# exit with status 77 is a skip, with the message in the output
+# otherwise, any output is a failure, even if exit status is 0
+
+# note: `set -e` is not active during the tests.
+
+find_scripts() {
+ # Helper to find all scripts in the tree
+ (
+ # Any non-binary file which contains a given shebang
+ git grep --cached -lIz '^#!.*'"$1"
+ shift
+ # Any file matching the provided globs
+ git ls-files -z "$@"
+ ) | sort -z | uniq -z
+}
+
+find_python_files() {
+ find_scripts 'python3' '*.py'
+}
+
+test_flake8() {
+ command -v flake8 >/dev/null || skip 'no flake8'
+ find_python_files | xargs -r -0 flake8
+}
+
+test_ruff() {
+ command -v ruff >/dev/null || skip 'no ruff'
+ find_python_files | xargs -r -0 ruff check --no-cache
+}
+
+if [ "${WITH_PARTIAL_TREE:-0}" = 0 ]; then
+ mypy_strict_files='
+ src/cockpit/__init__.py
+ src/cockpit/_version.py
+ src/cockpit/jsonutil.py
+ src/cockpit/protocol.py
+ src/cockpit/transports.py
+ '
+ test_mypy() {
+ command -v mypy >/dev/null || skip 'no mypy'
+ for pkg in systemd_ctypes ferny bei; do
+ test -e "src/cockpit/_vendor/${pkg}/__init__.py" || skip "no ${pkg}"
+ done
+ mypy --no-error-summary src/cockpit test/pytest
+ # test scripts individually, to avoid clashing on `__main__`
+ # also skip integration tests, they are too big and not annotated
+ find_scripts 'python3' "*.none" | grep -zv 'test/' | xargs -r -0 -n1 mypy --no-error-summary
+ mypy --no-error-summary --strict $mypy_strict_files
+ }
+
+ test_vulture() {
+ # vulture to find unused variables/functions
+ command -v vulture >/dev/null || skip 'no vulture'
+ find_python_files | xargs -r -0 vulture
+ }
+fi
+
+
+test_js_translatable_strings() {
+ # Translatable strings must be marked with _(""), not _('')
+
+ ! git grep -n -E "(gettext|_)\(['\`]" -- {src,pkg}/'*'.{js,jsx}
+}
+
+if [ "${WITH_PARTIAL_TREE:-0}" = 0 ]; then
+ test_eslint() {
+ test -x node_modules/.bin/eslint -a -x /usr/bin/node || skip 'no eslint'
+ find_scripts 'node' '*.js' '*.jsx' | xargs -0 node_modules/.bin/eslint
+ }
+fi
+
+test_stylelint() {
+ test -x node_modules/.bin/stylelint -a -x /usr/bin/node || skip 'no stylelint'
+ git ls-files -z '*.css' '*.scss' | xargs -r -0 node_modules/.bin/stylelint
+}
+
+test_no_translatable_attr() {
+ # Use of translatable attribute in HTML: should be 'translate' instead
+
+ ! git grep -n 'translatable=["'\'']yes' -- pkg doc
+}
+
+test_unsafe_security_policy() {
+ # It's dangerous to have 'unsafe-inline' or 'unsafe-eval' in our
+ # content-security-policy entries.
+
+ git grep -lIz -E 'content-security-policy.*(\*|unsafe)' 'pkg/*/manifest.json' | while read -d '' filename; do
+ if test ! -f "$(dirname ${filename})/content-security-policy.override"; then
+ echo "${filename} contains unsafe content security policy"
+ fi
+ done
+}
+
+test_json_verify() {
+ # Check all JSON files for validity
+
+ git ls-files -z '*.json' | while read -d '' filename; do
+ python3 -m json.tool "${filename}" /dev/null 2>&1 | sed "s@^@${filename}: @"
+ done
+}
+
+test_html_verify() {
+ # Check all HTML files for syntactic validity
+
+ git ls-files -z 'pkg/*.html' | while read -d '' filename; do
+ if ! python3 -c "import xml.etree.ElementTree as ET; ET.parse('${filename}')"; then
+ echo "${filename} contains invalid XML"
+ fi
+ done
+}
+
+test_include_config_h() {
+ # Every C file should #include "config.h" at the top
+
+ git ls-files -cz '*.c' | while read -d '' filename; do
+ if sed -n '/^#include "config.h"$/q1; /^\s*#/q;' "${filename}"; then
+ printf '%s: #include "config.h" is not the first line\n' "${filename}"
+ fi
+ done
+}
+
+### end of tests. start of machinery.
+
+skip() {
+ printf "%s\n" "$*"
+ exit 77
+}
+
+main() {
+ if [ $# = 0 ]; then
+ tap=''
+ elif [ $# = 1 -a "$1" = "--tap" ]; then
+ tap='1'
+ else
+ printf "usage: %s [--tap]\n" "$0" >&2
+ exit 1
+ fi
+
+ cd "${0%/*}/.."
+ if [ ! -e .git ]; then
+ echo '1..0 # SKIP not in a git checkout'
+ exit 0
+ fi
+
+ exit_status=0
+ counter=0
+
+ tests=($(compgen -A function 'test_'))
+ [ -n "${tap}" ] && printf "1..%d\n" "${#tests[@]}"
+
+ for test_function in "${tests[@]}"; do
+ path="/static-code/$(echo ${test_function} | tr '_' '-')"
+ counter=$((counter + 1))
+ fail=''
+ skip=''
+
+ # run the test, capturing its output and exit status
+ output="$(${test_function} 2>&1)" && test_status=0 || test_status=$?
+
+ if [ "${test_status}" = 77 ]; then
+ if [ -z "${tap}" ]; then
+ printf >&2 "WARNING: skipping %s: %s\n" "${path}" "${output}"
+ fi
+ skip=" # SKIP ${output}"
+ output=''
+ elif [ "${test_status}" != 0 -o -n "${output}" ]; then
+ exit_status=1
+ fail=1
+ fi
+
+ # Only print output on failures or --tap mode
+ [ -n "${tap}" -o -n "${fail}" ] || continue
+
+ # excluding the plan, this is the only output that we ever generate
+ printf "%s %d %s%s\n" "${fail:+not }ok" "${counter}" "${path}" "${skip}"
+ if [ -n "${output}" ]; then
+ printf "%s\n" "${output}" | sed -e 's/^/# /'
+ fi
+ done
+
+ exit "${exit_status}"
+}
+
+main "$@"