Skip to content
This repository has been archived by the owner on Oct 2, 2024. It is now read-only.

add doctests to test suite #1744

Merged
merged 5 commits into from
Oct 31, 2023
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ a.out
/lib/version.py
/lib/version.sh
/lib/version.txt
/test/build/30_doctest-auto.bats
/test/force-auto.bats
/test/docs-sane
/test/doctest
/test/fixtures/symlink-to-tmp
/test/force-auto
/test/make-perms-test
Expand Down
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ install-exec-hook:
$(DESTDIR)@bindir@/ch-image \
$(DESTDIR)@bindir@/ch-run-oci \
$(DESTDIR)@bindir@/ch-test \
$(DESTDIR)@libdir@/charliecloud/base.sh; \
$(DESTDIR)@libdir@/charliecloud/base.sh \
$(DESTDIR)@libexecdir@/charliecloud/doctest; \
do \
sed -Ei -e 's|^(ch_lib ?= ?).+/lib"?$$|\1"@libdir@/charliecloud"|' \
-e 's|^(CHTEST_DIR=).+$$|\1@libexecdir@/charliecloud|' \
Expand Down
6 changes: 3 additions & 3 deletions lib/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,11 @@ def strip(self, left=0, right=0):

>>> a = Path("/a/b/c")
>>> a.strip(left=1)
Path("a/b/c")
Path('a/b/c')
>>> a.strip(right=1)
Path("/a/b")
Path('/a/b')
>>> a.strip(left=1, right=1)
Path("a/b")
Path('a/b')

It is an error if I don’t have at least left + right components,
i.e., you can strip a path down to nothing but not further."""
Expand Down
3 changes: 2 additions & 1 deletion misc/loc
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,9 @@ find ./.github ./examples ./test -type f -a \( \
-o -path ./test/fixtures/README \
-o -path ./.github/PERUSEME \
-o -path ./examples/chtest/printns \
-o -path ./test/common.bash \
-o -path ./test/approved-trailing-whitespace \
-o -path ./test/common.bash \
-o -path ./test/doctest-auto \
-o -path ./test/old-storage \
-o -path ./test/sotest/files_inferrable.txt \
-o -path ./test/whiteout \) | sort > /tmp/loc.test
Expand Down
22 changes: 17 additions & 5 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ Build.centos7xz \
Build.docker_pull \
Build.missing \
docs-sane \
doctest \
doctest-auto \
force-auto \
make-perms-test \
old-storage

Expand All @@ -68,8 +71,10 @@ sotest/libsotest.so.1.0 \
sotest/sotest

CLEANFILES = $(sobuilts) \
docs-sane force-auto make-perms-test \
force-auto.bats
docs-sane \
doctest build/30_doctest-auto.bats \
force-auto force-auto.bats \
make-perms-test

if ENABLE_TEST
nobase_test_DATA = $(testfiles)
Expand All @@ -79,6 +84,9 @@ if ENABLE_CH_IMAGE # this means we have Python
nobase_test_DATA += force-auto.bats
force-auto.bats: force-auto
./$< > $@
nobase_test_DATA += build/30_doctest-auto.bats
build/30_doctest-auto.bats: doctest-auto
./$< > $@
endif
# See comment about symlinks in examples/Makefile.am.
all-local:
Expand All @@ -93,12 +101,16 @@ uninstall-hook:
rmdir $(DESTDIR)$(testdir)/fixtures || true
rmdir $$(find $(pkglibexecdir) -type d | sort -r)
endif
EXTRA_DIST = $(testfiles) $(testfiles_exec) \
docs-sane.py.in force-auto.py.in make-perms-test.py.in
EXTRA_DIST = $(testfiles) \
$(testfiles_exec) \
docs-sane.py.in \
doctest.py.in \
force-auto.py.in \
make-perms-test.py.in
EXTRA_SCRIPTS = $(sobuilts)

## Python scripts - need text processing
docs-sane force-auto make-perms-test: %: %.py.in
docs-sane doctest force-auto make-perms-test: %: %.py.in
rm -f $@
sed -E 's|%PYTHON_SHEBANG%|@PYTHON_SHEBANG@|' < $< > $@
chmod +rx,-w $@ # respects umask
Expand Down
32 changes: 32 additions & 0 deletions test/doctest-auto
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

# Print (on stdout) BATS tests to run doctests on each file in lib/.

set -e -o pipefail

cat <<EOF
# AUTOGENERATED -- DO NOT EDIT

load ../common

setup () {
scope standard
[[ \$CH_TEST_BUILDER = ch-image ]] || skip 'ch-image only'
}

EOF


for f in ../lib/*.py; do
m=$(basename "$f")
m=${m%%.py}
if [[ $m == version ]]; then
continue
fi
cat <<EOF

@test 'doctest: $m' {
./doctest $m
}
EOF
done
82 changes: 82 additions & 0 deletions test/doctest.py.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!%PYTHON_SHEBANG%

import doctest
import fnmatch
import importlib.util
import os
import re
import sys

usage = """\
Usage:

$ test/doctest MODULE [OBJECT_REGEX]

Run doctests in Charliecloud Python MODULE (in lib/). If OBJECT_REGEX given,
only fun tests on objects with names (excluding the module name) matching that
regular expression (default all objects). Exits unsuccessfully on first
failure."""


# Command line arguments.

try:
module_name = sys.argv[1]
except IndexError:
module_name = "--help"
try:
object_re = sys.argv[2]
except IndexError:
object_re = ".*"
if (module_name in ("-h", "--help", "-?")):
print(usage, file=sys.stderr)
sys.exit(1) # help message is not a successful test


# Import target module.

ch_lib = os.path.dirname(os.path.abspath(__file__)) + "/../lib"
sys.path.insert(0, ch_lib)
import charliecloud as ch # avoid circular import problems
print("imported %s from %s" % (ch.__name__, ch.__file__))
module = importlib.import_module(module_name)
print("imported %s from %s" % (module.__name__, module.__file__))


# Locate tests to run.
# see: https://github.com/python/cpython/blob/73a003f/Lib/doctest.py#L1905

tests_all = doctest.DocTestFinder().find(module)
for test in tests_all:
test.name_short = re.sub(r"^[a-z_]+\.", "", test.name)
tests_nonempty = [i for i in tests_all if len(i.examples) > 0]
tests = [i for i in tests_nonempty if re.search(object_re, i.name_short)]
print("will run %d/%d tests" % (len(tests), len(tests_nonempty)))


# Run tests.

out = ""
def out_save(text):
global out
out += text
runner = doctest.DocTestRunner(optionflags=( doctest.DONT_ACCEPT_TRUE_FOR_1
| doctest.ELLIPSIS))
for test in tests:
print("%s (%d examples) ... " % (test.name_short, len(test.examples)),
end="")
out = ""
results = runner.run(test, out=out_save)
assert (results.attempted == len(test.examples))
if (results.failed == 0):
print("ok")
else:
print("%d failed" % results.failed)
print(out)
print("big L, stopping tests")
sys.exit(1)


# Summarize.

print("all tests passed")
10 changes: 5 additions & 5 deletions test/force-auto.py.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#!%PYTHON_SHEBANG%

# This script generates a BATS file to exercise “ch-image build --force”
# across a variety of distributions. It's used by Makefile.am.
# across a variety of distributions. Its used by Makefile.am.
#
# About each distribution, we remember:
#
Expand All @@ -25,12 +25,12 @@
# This would appear to yield 3×2×4 = 24 tests per distribution. However:
#
# 1. We only try pre-prepared images for “really need” commands with --force
# given, to save time, so it's at most 9 potential tests.
# given, to save time, so its at most 9 potential tests.
#
# 2. The pre-preparation step doesn't make sense for some distros or for
# 2. The pre-preparation step doesnt make sense for some distros or for
# --force=seccomp.
#
# 3. We've not yet determined an “apparently need” command for some distros.
# 3. Weve not yet determined an “apparently need” command for some distros.
#
# Bottom line, the number of tests per distro varies. See the code below for
# specific details.
Expand Down