diff options
| author | Egor Tensin <egor@tensin.name> | 2026-09-29 23:44:16 +0500 |
|---|---|---|
| committer | Egor Tensin <egor@tensin.name> | 2026-09-29 23:45:40 +0500 |
| commit | eec673b5c0122e359bf00b0136783a32e8a77df9 (patch) | |
| tree | 61457ccf0d65331f1411b28aa7ba2ad964191ba2 | |
| parent | remove annoying trailing period from repo URLs (diff) | |
| download | vk-scripts-1.2.0.tar.gz vk-scripts-1.2.0.zip | |
test: rework using the approach from my other projectsv1.2.0
The tests are now run using a framework common to my other projects,
like config-links & tag-release.
| -rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
| -rwxr-xr-x | test/bin/main.sh | 19 | ||||
| -rwxr-xr-x | test/bin/mutuals.sh | 23 | ||||
| -rwxr-xr-x | test/bin/sessions.sh | 69 | ||||
| -rwxr-xr-x | test/bin/status.sh | 60 | ||||
| -rwxr-xr-x | test/bin/status_once.sh | 22 | ||||
| -rw-r--r-- | test/data/test_db.csv (renamed from test/share/test_db.csv) | 0 | ||||
| -rw-r--r-- | test/lib/log.sh | 139 | ||||
| -rw-r--r--[-rwxr-xr-x] | test/lib/test.sh | 32 | ||||
| -rwxr-xr-x | test/main.sh | 77 | ||||
| -rw-r--r-- | test/tst-mutuals.sh | 38 | ||||
| -rw-r--r-- | test/tst-sessions.sh | 51 | ||||
| -rw-r--r-- | test/tst-status-once.sh | 25 | ||||
| -rw-r--r-- | test/tst-status.sh | 45 |
14 files changed, 391 insertions, 211 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8982455..52fa895 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: vk-status --version vk-sessions --version - name: Run tests - run: ./test/bin/main.sh + run: ./test/main.sh publish_pypi: needs: [lint, test] diff --git a/test/bin/main.sh b/test/bin/main.sh deleted file mode 100755 index 6b6e409..0000000 --- a/test/bin/main.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2019 Egor Tensin <egor@tensin.name> -# This file is part of the "VK scripts" project. -# For details, see https://github.com/egor-tensin/vk-scripts -# Distributed under the MIT License. - -set -o errexit -o nounset -o pipefail - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir - -"$script_dir/mutuals.sh" - -"$script_dir/sessions.sh" - -"$script_dir/status_once.sh" -"$script_dir/status.sh" diff --git a/test/bin/mutuals.sh b/test/bin/mutuals.sh deleted file mode 100755 index b5d3eb4..0000000 --- a/test/bin/mutuals.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2019 Egor Tensin <egor@tensin.name> -# This file is part of the "VK scripts" project. -# For details, see https://github.com/egor-tensin/vk-scripts -# Distributed under the MIT License. - -set -o errexit -o nounset -o pipefail - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir - -test_users() { - "$script_dir/../lib/test.sh" vk.mutuals --format csv "$@" - "$script_dir/../lib/test.sh" vk.mutuals --format json "$@" -} - -main() { - test_users kreed58 korya_mc -} - -main diff --git a/test/bin/sessions.sh b/test/bin/sessions.sh deleted file mode 100755 index d952831..0000000 --- a/test/bin/sessions.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2019 Egor Tensin <egor@tensin.name> -# This file is part of the "VK scripts" project. -# For details, see https://github.com/egor-tensin/vk-scripts -# Distributed under the MIT License. - -set -o errexit -o nounset -o pipefail - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir -script_name="$( basename -- "${BASH_SOURCE[0]}" )" -readonly script_name - -readonly db_path="$script_dir/../share/test_db.csv" - -dump() { - local msg - for msg; do - echo "$script_name: $msg" - done -} - -test_output() { - local output_path - output_path="$( mktemp --dry-run )" - - local rm_aux_files - rm_aux_files="$( printf -- 'rm -f -- %q' "$output_path" )" - - trap "$rm_aux_files" RETURN - - "$script_dir/../lib/test.sh" vk.tracking.sessions "$@" "$db_path" "$output_path" - - if file --brief --dereference --mime -- "$output_path" | grep --quiet -- 'charset=binary$'; then - dump 'Output is a binary file, not going to show that' - return 0 - fi - - cat "$output_path" -} - -group_by() { - test_output --output-format csv --group-by "$@" - test_output --output-format json --group-by "$@" - test_output --output-format plot --group-by "$@" -} - -online_sessions() { - group_by user - group_by hour - group_by date - group_by weekday -} - -fix_matplotlib() { - # Get rid of: - # tkinter.TclError: no display name and no $DISPLAY environment variable - mkdir -p -- ~/.config/matplotlib - echo 'backend: Agg' > ~/.config/matplotlib/matplotlibrc -} - -main() { - fix_matplotlib - online_sessions -} - -main diff --git a/test/bin/status.sh b/test/bin/status.sh deleted file mode 100755 index ab12805..0000000 --- a/test/bin/status.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2019 Egor Tensin <egor@tensin.name> -# This file is part of the "VK scripts" project. -# For details, see https://github.com/egor-tensin/vk-scripts -# Distributed under the MIT License. - -set -o errexit -o nounset -o pipefail - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir -script_name="$( basename -- "${BASH_SOURCE[0]}" )" -readonly script_name - -dump() { - local msg - for msg; do - echo "$script_name: $msg" - done -} - -test_users() { - local log_path - log_path="$( mktemp )" - local db_path - db_path="$( mktemp --dry-run )" - - local rm_aux_files - rm_aux_files="$( printf -- 'rm -f -- %q %q' "$log_path" "$db_path" )" - trap "$rm_aux_files" RETURN - - "$script_dir/../lib/test.sh" vk.tracking.status "$@" --log "$log_path" --format csv --output "$db_path" & - local pid="$!" - - sleep 3 - dump "Log file path: $log_path" - dump "DB file path: $db_path" - dump "PID: $pid" - - local timeout=10 - dump "Sleeping for $timeout seconds..." - sleep "$timeout" - - dump 'Terminating track_status.py...' - kill "$pid" - dump 'Waiting for track_status.py to terminate...' - wait "$pid" || true - - dump "Log file:" - cat "$log_path" - dump "DB:" - cat "$db_path" -} - -main() { - test_users egor.tensin -} - -main "$@" diff --git a/test/bin/status_once.sh b/test/bin/status_once.sh deleted file mode 100755 index 8d8878d..0000000 --- a/test/bin/status_once.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2019 Egor Tensin <egor@tensin.name> -# This file is part of the "VK scripts" project. -# For details, see https://github.com/egor-tensin/vk-scripts -# Distributed under the MIT License. - -set -o errexit -o nounset -o pipefail - -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir - -test_users() { - "$script_dir/../lib/test.sh" vk.tracking.status --only-once "$@" -} - -main() { - test_users egor.tensin -} - -main diff --git a/test/share/test_db.csv b/test/data/test_db.csv index 2b087a8..2b087a8 100644 --- a/test/share/test_db.csv +++ b/test/data/test_db.csv diff --git a/test/lib/log.sh b/test/lib/log.sh new file mode 100644 index 0000000..48a34cc --- /dev/null +++ b/test/lib/log.sh @@ -0,0 +1,139 @@ +# Copyright (c) 2026 Egor Tensin <egor@tensin.name> +# This file is part of the "VK scripts" project. +# For details, see https://github.com/egor-tensin/vk-scripts +# Distributed under the MIT License. + +log() { + local msg + for msg; do + echo -e "$test_file | $msg" >&2 + done +} + +log_run() { + if [ "$#" -lt 1 ]; then + log "usage: ${FUNCNAME[0]} ARGV0 [ARG...]" + return 1 + fi + + local msg='Running:' + msg="$msg$( printf -- ' %q' "$@" )" + log "$msg" +} + +fail() { + local msg + for msg; do + log "FAIL: $msg" + done +} + +fail_details() { + local msg + for msg; do + fail " $msg" + done +} + +log_numof_tests_ok=0 +log_numof_tests_failed=0 +declare -a log_tests_failed=() +declare -A log_tests_should_fail=() + +log_test_start() { + if [ "$#" -ne 1 ]; then + echo "usage: ${FUNCNAME[0]} TEST_FILE" >&2 + return 1 + fi + + local test_file="$1" + + if [ -n "${test_should_fail:-}" ]; then + log_tests_should_fail["$test_file"]=1 + fi + + cat <<EOF >&2 + +====================================================================== +TEST: $test_file +====================================================================== +EOF + + log "Should fail: ${test_should_fail:-No}" +} + +log_test_ok() { + if [ "$#" -ne 1 ]; then + echo "usage: ${FUNCNAME[0]} TEST_FILE" >&2 + return 1 + fi + + log_numof_tests_ok=$((log_numof_tests_ok + 1)) + + cat <<EOF >&2 +---------------------------------------------------------------------- +OK: $1 +---------------------------------------------------------------------- +EOF +} + +log_test_fail() { + if [ "$#" -ne 1 ]; then + echo "usage: ${FUNCNAME[0]} TEST_FILE" >&2 + return 1 + fi + + log_numof_tests_failed=$((log_numof_tests_failed + 1)) + log_tests_failed+=("$1") + + cat <<EOF >&2 +---------------------------------------------------------------------- +FAIL: $1 +---------------------------------------------------------------------- +EOF +} + +log_test_finish() { + if [ "$#" -ne 2 ]; then + echo "usage: ${FUNCNAME[0]} TEST_FILE EXIT_CODE" >&2 + return 1 + fi + + local test_file="$1" + local ret="$2" + local test_should_fail + test_should_fail="${log_tests_should_fail["$test_file"]:+x}" + + if [ "$ret" -eq 0 ] || [ -n "$test_should_fail" ]; then + log_test_ok "$test_file" + else + log_test_fail "$test_file" + fi +} + +log_tests_summary() { + cat <<EOF >&2 + +====================================================================== +SUMMARY +====================================================================== +OK: $log_numof_tests_ok +FAIL: $log_numof_tests_failed +EOF + + local test_file + for test_file in ${log_tests_failed[@]+"${log_tests_failed[@]}"}; do + cat <<EOF >&2 +FAIL: $test_file +EOF + done + + cat <<EOF >&2 +---------------------------------------------------------------------- +EOF + + local ret=0 + [ "$log_numof_tests_failed" -gt 0 ] && ret=1 + + return "$ret" +} diff --git a/test/lib/test.sh b/test/lib/test.sh index 56b3235..30c4ce1 100755..100644 --- a/test/lib/test.sh +++ b/test/lib/test.sh @@ -1,27 +1,25 @@ -#!/usr/bin/env bash - # Copyright (c) 2020 Egor Tensin <egor@tensin.name> # This file is part of the "VK scripts" project. # For details, see https://github.com/egor-tensin/vk-scripts # Distributed under the MIT License. -set -o errexit -o nounset -o pipefail +test_should_fail= +test_root_dir= -script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" -script_dir="$( cd -- "$script_dir" && pwd )" -readonly script_dir +test_setup() { + test_root_dir="$( mktemp -d )" -run_test() { - local arg - echo - echo ====================================================================== - for arg; do - echo -n "$arg " - done - echo - echo ====================================================================== + log "Root directory: $test_root_dir" +} - PYTHONPATH="$script_dir/../.." python -m "$@" +test_cleanup_default() { + if [ -n "$test_root_dir" ]; then + log "Removing test's root directory: $test_root_dir" + rm -rf -- "$test_root_dir" + fi } -run_test "$@" +test_run_module() { + log_run python -m "$@" + PYTHONPATH="$script_dir/.." python -m "$@" +} diff --git a/test/main.sh b/test/main.sh new file mode 100755 index 0000000..07889ac --- /dev/null +++ b/test/main.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +# Copyright (c) 2026 Egor Tensin <egor@tensin.name> +# This file is part of the "VK scripts" project. +# For details, see https://github.com/egor-tensin/vk-scripts +# Distributed under the MIT License. + +set -o errexit -o nounset -o pipefail +shopt -s inherit_errexit lastpipe + +script_dir="$( dirname -- "${BASH_SOURCE[0]}" )" +script_dir="$( cd -- "$script_dir" && pwd )" +readonly script_dir +script_name="$( basename -- "${BASH_SOURCE[0]}" )" +readonly script_name + +source "$script_dir/lib/log.sh" + +run_test_file() { + local test_file + for test_file; do + source "$script_dir/lib/test.sh" + source "$script_dir/$test_file" + + log_test_start "$test_file" + + set +e + ( + set -o errexit -o nounset -o pipefail + shopt -s inherit_errexit lastpipe + + if [ "$( type -t test_cleanup )" == function ]; then + trap test_cleanup EXIT + else + trap test_cleanup_default EXIT + fi + + test_run + ) + local ret="$?" + set -e + + log_test_finish "$test_file" "$ret" + done +} + +main() { + if [ "$#" -gt 1 ]; then + echo "usage: $script_name [PATTERN]" >&2 + return 1 + fi + + local -a test_files=() + local test_file + + local pattern='*' + [ "$#" -gt 0 ] && pattern="$1" + + find "$script_dir" \ + -mindepth 1 \ + -maxdepth 1 \ + -type f \ + -regex '.*/tst-.*\.sh$' \ + -regextype posix-basic \ + -name "$pattern" \ + -printf '%P\0' \ + | sort -z \ + | \ + while IFS= read -d '' -r test_file; do + test_files+=("$test_file") + done + + run_test_file ${test_files[@]+"${test_files[@]}"} + log_tests_summary +} + +main "$@" diff --git a/test/tst-mutuals.sh b/test/tst-mutuals.sh new file mode 100644 index 0000000..3f40780 --- /dev/null +++ b/test/tst-mutuals.sh @@ -0,0 +1,38 @@ +test_run() { + test_setup + + local users=(kreed58 korya_mc) + + test_run_module vk.mutuals --format csv "${users[@]}" > "$test_root_dir/mutuals.csv" + test_run_module vk.mutuals --format json "${users[@]}" > "$test_root_dir/mutuals.json" + + local expected=10 + + local actual_csv + actual_csv="$( cat -- "$test_root_dir/mutuals.csv" | wc -l )" + log "Mutuals in CSV: $actual_csv" + + if [ "$actual_csv" -lt "$expected" ]; then + fail "Expected a lot more mutual friends in CSV output" + fail_details "Expected: $expected" + fail_details "Actual: $actual_csv" + return 1 + fi + + local actual_json + actual_json="$( grep -F -- '"id":' "$test_root_dir/mutuals.json" | wc -l )" + log "Mutuals in JSON: $actual_json" + + if [ "$actual_json" -lt "$expected" ]; then + fail "Expected a lot more mutual friends in JSON output" + fail_details "Expected: $expected" + fail_details "Actual: $actual_json" + return 1 + fi + + if [ "$actual_csv" -ne "$actual_json" ]; then + fail "Number of mutuals in CSV unequal to that in JSON" + fail_details "Can technically happen, because it's kind of a race" + return 1 + fi +} diff --git a/test/tst-sessions.sh b/test/tst-sessions.sh new file mode 100644 index 0000000..2885d0c --- /dev/null +++ b/test/tst-sessions.sh @@ -0,0 +1,51 @@ +test_db_path="$script_dir/data/test_db.csv" +readonly test_db_path + +_test_format() { + local output_path + output_path="$( mktemp --tmpdir="$test_root_dir" )" + + test_run_module vk.tracking.sessions "$@" "$test_db_path" "$output_path" + + if [ ! -s "$output_path" ]; then + fail "Output file '$output_path' either doesn't exists or is empty" + return 1 + fi + + if file --brief --dereference --mime -- "$output_path" | grep --quiet -- 'charset=binary$'; then + log 'Output is a binary file, not going to show that' + return 0 + fi + + cat -- "$output_path" >&2 +} + +_test_group_by() { + local group_by + for group_by; do + local format + for format in csv json plot; do + _test_format --output-format "$format" --group-by "$group_by" + done + done +} + +_test_main() { + _test_group_by user + _test_group_by hour + _test_group_by date + _test_group_by weekday +} + +_fix_matplotlib() { + # Get rid of: + # tkinter.TclError: no display name and no $DISPLAY environment variable + mkdir -p -- ~/.config/matplotlib + echo 'backend: Agg' > ~/.config/matplotlib/matplotlibrc +} + +test_run() { + test_setup + _fix_matplotlib + _test_main +} diff --git a/test/tst-status-once.sh b/test/tst-status-once.sh new file mode 100644 index 0000000..1623d25 --- /dev/null +++ b/test/tst-status-once.sh @@ -0,0 +1,25 @@ +test_run() { + test_setup + + local users=(egor.tensin kreed58) + local db_path + db_path="$test_root_dir/db.csv" + + test_run_module vk.tracking.status --only-once -o "$db_path" "${users[@]}" + + if [ ! -s "$db_path" ]; then + fail "Database file '$db_path' either doesn't exists or is empty" + return 1 + fi + + log "Database:" + cat -- "$db_path" >&2 + + local lines + lines="$( cat -- "$db_path" | wc -l )" + + if [ "$lines" -ne 2 ]; then + fail "Database '$db_path' has the wrong number of entries" + return 1 + fi +} diff --git a/test/tst-status.sh b/test/tst-status.sh new file mode 100644 index 0000000..e967ae5 --- /dev/null +++ b/test/tst-status.sh @@ -0,0 +1,45 @@ +test_run() { + test_setup + + local users=(egor.tensin) + + local log_path + log_path="$test_root_dir/status.log" + local db_path + db_path="$test_root_dir/status.csv" + + test_run_module vk.tracking.status \ + --log "$log_path" \ + --format csv \ + --output "$db_path" \ + "${users[@]}" & + local pid="$!" + + sleep 3 + log "Log file: $log_path" + log "Database file: $db_path" + log "PID: $pid" + + local timeout=10 + log "Sleeping for $timeout seconds..." + sleep "$timeout" + + log 'Terminating track_status.py...' + kill "$pid" + log 'Waiting for track_status.py to terminate...' + wait "$pid" || true + + if [ ! -s "$log_path" ]; then + fail "Log file '$log_path' either doesn't exists or is empty" + return 1 + fi + log 'Log file:' + cat -- "$log_path" >&2 + + if [ ! -s "$db_path" ]; then + fail "Database file '$db_path' either doesn't exists or is empty" + return 1 + fi + log 'Database:' + cat -- "$db_path" >&2 +} |
