mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-11 20:48:49 +00:00
Rewrite test driver in python
This commit is contained in:
parent
8304fd0fd0
commit
f1cf64fba0
9 changed files with 221 additions and 14 deletions
|
@ -100,8 +100,8 @@ foreach(CHECK ${FISH_CHECKS})
|
||||||
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
||||||
get_filename_component(CHECK ${CHECK} NAME_WE)
|
get_filename_component(CHECK ${CHECK} NAME_WE)
|
||||||
add_test(NAME ${CHECK_NAME}
|
add_test(NAME ${CHECK_NAME}
|
||||||
COMMAND env FISHDIR=${CMAKE_CURRENT_BINARY_DIR}/ ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
|
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/tests/test.fish ${CHECK}
|
checks/${CHECK}.fish
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
||||||
)
|
)
|
||||||
set_tests_properties(${CHECK_NAME} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
set_tests_properties(${CHECK_NAME} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
||||||
|
@ -113,8 +113,8 @@ FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
|
||||||
foreach(PEXPECT ${PEXPECTS})
|
foreach(PEXPECT ${PEXPECTS})
|
||||||
get_filename_component(PEXPECT ${PEXPECT} NAME)
|
get_filename_component(PEXPECT ${PEXPECT} NAME)
|
||||||
add_test(NAME ${PEXPECT}
|
add_test(NAME ${PEXPECT}
|
||||||
COMMAND env FISHDIR=${CMAKE_CURRENT_BINARY_DIR}/ ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
|
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/tests/interactive.fish ${PEXPECT}
|
pexpects/${PEXPECT}
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
||||||
)
|
)
|
||||||
set_tests_properties(${PEXPECT} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
set_tests_properties(${PEXPECT} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#RUN: fish=%fish %fish %s | %fish %filter-control-sequences
|
#RUN: fish=%fish %fish %s
|
||||||
set -g PATH
|
set -g PATH
|
||||||
$fish -c "nonexistent-command-1234 banana rama"
|
$fish -c "nonexistent-command-1234 banana rama"
|
||||||
#CHECKERR: fish: Unknown command: nonexistent-command-1234
|
#CHECKERR: fish: Unknown command: nonexistent-command-1234
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#RUN: %fish --interactive %s | %fish %filter-control-sequences
|
#RUN: %fish --interactive %s
|
||||||
# ^ interactive so we can do `complete`
|
# ^ interactive so we can do `complete`
|
||||||
mkdir -p __fish_complete_directories/
|
mkdir -p __fish_complete_directories/
|
||||||
cd __fish_complete_directories
|
cd __fish_complete_directories
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#RUN: %fish -i %s | %fish %filter-control-sequences
|
#RUN: %fish -i %s
|
||||||
# Note: ^ this is interactive so we test interactive behavior,
|
# Note: ^ this is interactive so we test interactive behavior,
|
||||||
# e.g. the fish_git_prompt variable handlers test `status is-interactive`.
|
# e.g. the fish_git_prompt variable handlers test `status is-interactive`.
|
||||||
#REQUIRES: command -v git
|
#REQUIRES: command -v git
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#RUN: fish=%fish %fish %s | %fish %filter-control-sequences
|
#RUN: fish=%fish %fish %s
|
||||||
|
|
||||||
$fish -c "echo 1.2.3.4."
|
$fish -c "echo 1.2.3.4."
|
||||||
# CHECK: 1.2.3.4.
|
# CHECK: 1.2.3.4.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: fish=%fish filter_ctrls=%filter-control-sequences %fish %s
|
# RUN: fish=%fish %fish %s
|
||||||
# Set term again explicitly to ensure behavior.
|
# Set term again explicitly to ensure behavior.
|
||||||
set -gx TERM xterm
|
set -gx TERM xterm
|
||||||
# Read with no vars is not an error
|
# Read with no vars is not an error
|
||||||
|
@ -248,7 +248,7 @@ if test (string length "$x") -ne $fish_read_limit
|
||||||
end
|
end
|
||||||
|
|
||||||
# Confirm reading non-interactively works -- \#4206 regression
|
# Confirm reading non-interactively works -- \#4206 regression
|
||||||
echo abc\ndef | $fish -i -c 'read a; read b; set --show a; set --show b' | $fish $filter_ctrls
|
echo abc\ndef | $fish -i -c 'read a; read b; set --show a; set --show b'
|
||||||
#CHECK: $a: set in global scope, unexported, with 1 elements
|
#CHECK: $a: set in global scope, unexported, with 1 elements
|
||||||
#CHECK: $a[1]: |abc|
|
#CHECK: $a[1]: |abc|
|
||||||
#CHECK: $b: set in global scope, unexported, with 1 elements
|
#CHECK: $b: set in global scope, unexported, with 1 elements
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#RUN: fish=%fish filter_ctrls=%filter-control-sequences %fish %s
|
#RUN: fish=%fish %fish %s
|
||||||
# Some tests of the "return" builtin.
|
# Some tests of the "return" builtin.
|
||||||
|
|
||||||
$fish -c 'return 5'
|
$fish -c 'return 5'
|
||||||
|
@ -21,7 +21,7 @@ begin
|
||||||
# but not bar
|
# but not bar
|
||||||
echo $status
|
echo $status
|
||||||
# CHECK: 69
|
# CHECK: 69
|
||||||
end | $fish $filter_ctrls
|
end
|
||||||
|
|
||||||
# Verify negative return values don't cause UB and never map to 0
|
# Verify negative return values don't cause UB and never map to 0
|
||||||
function empty_return
|
function empty_return
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: env FISH=%fish filter_ctrls=%filter-control-sequences %fish %s
|
# RUN: env FISH=%fish %fish %s
|
||||||
# Environment variable tests
|
# Environment variable tests
|
||||||
|
|
||||||
# Test if variables can be properly set
|
# Test if variables can be properly set
|
||||||
|
@ -367,7 +367,7 @@ begin
|
||||||
env SHLVL=" 3" $FISH -ic 'echo SHLVL: $SHLVL'
|
env SHLVL=" 3" $FISH -ic 'echo SHLVL: $SHLVL'
|
||||||
# CHECK: SHLVL: 4
|
# CHECK: SHLVL: 4
|
||||||
# CHECK: SHLVL: 4
|
# CHECK: SHLVL: 4
|
||||||
end | $FISH $filter_ctrls
|
end
|
||||||
|
|
||||||
# Non-interactive fish doesn't touch $SHLVL
|
# Non-interactive fish doesn't touch $SHLVL
|
||||||
env SHLVL=2 $FISH -c 'echo SHLVL: $SHLVL'
|
env SHLVL=2 $FISH -c 'echo SHLVL: $SHLVL'
|
||||||
|
|
207
tests/test_driver.py
Executable file
207
tests/test_driver.py
Executable file
|
@ -0,0 +1,207 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import littlecheck
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pexpect
|
||||||
|
|
||||||
|
PEXPECT = True
|
||||||
|
except ImportError:
|
||||||
|
PEXPECT = False
|
||||||
|
|
||||||
|
RESET = "\033[0m"
|
||||||
|
GREEN = "\033[32m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
RED = "\033[31m"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: test_driver.py FISH_DIRECTORY TESTS")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fishdir = Path(sys.argv[1]).absolute()
|
||||||
|
if not fishdir.is_dir():
|
||||||
|
fishdir = fishdir.parent
|
||||||
|
script_path = Path(__file__).parent
|
||||||
|
|
||||||
|
failcount = 0
|
||||||
|
passcount = 0
|
||||||
|
skipcount = 0
|
||||||
|
def_subs = {"%": "%"}
|
||||||
|
lconfig = littlecheck.Config()
|
||||||
|
lconfig.colorize = sys.stdout.isatty()
|
||||||
|
lconfig.progress = True
|
||||||
|
|
||||||
|
for bin in ["fish", "fish_indent", "fish_key_reader"]:
|
||||||
|
if os.path.exists(fishdir / bin):
|
||||||
|
def_subs[bin] = str(fishdir / bin)
|
||||||
|
else:
|
||||||
|
print(f"Binary does not exist: {bin}")
|
||||||
|
return 127
|
||||||
|
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
files = [(os.path.abspath(path), path) for path in sys.argv[2:]]
|
||||||
|
else:
|
||||||
|
files = [
|
||||||
|
(os.path.abspath(path), path.relative_to(script_path))
|
||||||
|
for path in script_path.glob("checks/*.fish")
|
||||||
|
]
|
||||||
|
files += [
|
||||||
|
(os.path.abspath(path), path.relative_to(script_path))
|
||||||
|
for path in script_path.glob("pexpects/*.py")
|
||||||
|
]
|
||||||
|
|
||||||
|
# Set up tempdir
|
||||||
|
# "delete=" was added in 3.12.
|
||||||
|
home = tempfile.TemporaryDirectory(prefix="fishtest-")
|
||||||
|
xdg_config = home.name + "/xdg_config_home"
|
||||||
|
func_dir = xdg_config + "/fish/functions"
|
||||||
|
os.makedirs(func_dir)
|
||||||
|
os.makedirs(xdg_config + "/fish/conf.d/")
|
||||||
|
for func in (script_path / "test_functions").glob("*.fish"):
|
||||||
|
shutil.copy(func, func_dir + "/" + func.parts[-1])
|
||||||
|
shutil.copy(
|
||||||
|
script_path / "interactive.config", xdg_config + "/fish/conf.d/interactive.fish"
|
||||||
|
)
|
||||||
|
|
||||||
|
xdg_data = home.name + "/xdg_data_home"
|
||||||
|
os.makedirs(xdg_data)
|
||||||
|
xdg_runtime = home.name + "/xdg_runtime_home"
|
||||||
|
os.makedirs(xdg_runtime)
|
||||||
|
xdg_cache = home.name + "/xdg_cache_home"
|
||||||
|
os.makedirs(xdg_cache)
|
||||||
|
tmp = home.name + "/temp"
|
||||||
|
os.makedirs(tmp)
|
||||||
|
|
||||||
|
# Compile fish_test_helper if necessary.
|
||||||
|
# If we're run multiple times, keep this around to save time.
|
||||||
|
# TODO: It's cheesy to leave this in the current dir
|
||||||
|
if not os.path.exists("fish_test_helper"):
|
||||||
|
comp = subprocess.run(
|
||||||
|
["cc", script_path / "fish_test_helper.c", "-o", "fish_test_helper"]
|
||||||
|
)
|
||||||
|
shutil.copy("fish_test_helper", home.name + "/fish_test_helper")
|
||||||
|
def_subs.update({"fish_test_helper": home.name + "/fish_test_helper"})
|
||||||
|
|
||||||
|
# unset LANG, TERM, ...
|
||||||
|
for var in [
|
||||||
|
"XDG_DATA_DIRS",
|
||||||
|
"LANGUAGE",
|
||||||
|
"COLORTERM",
|
||||||
|
"KONSOLE_PROFILE_NAME",
|
||||||
|
"KONSOLE_VERSION",
|
||||||
|
"TERM_PROGRAM",
|
||||||
|
"TERM_PROGRAM_VERSION",
|
||||||
|
"VTE_VERSION",
|
||||||
|
]:
|
||||||
|
if var in os.environ:
|
||||||
|
del os.environ[var]
|
||||||
|
langvars = [key for key in os.environ.keys() if key.startswith("LC_")]
|
||||||
|
for key in langvars:
|
||||||
|
del os.environ[key]
|
||||||
|
|
||||||
|
os.environ.update(
|
||||||
|
{
|
||||||
|
"HOME": home.name,
|
||||||
|
"TMPDIR": tmp,
|
||||||
|
"FISH_FAST_FAIL": "1",
|
||||||
|
"FISH_UNIT_TESTS_RUNNING": "1",
|
||||||
|
"XDG_CONFIG_HOME": xdg_config,
|
||||||
|
"XDG_DATA_HOME": xdg_data,
|
||||||
|
"XDG_RUNTIME_DIR": xdg_runtime,
|
||||||
|
"XDG_CACHE_HOME": xdg_cache,
|
||||||
|
"fish_test_helper": home.name + "/fish_test_helper",
|
||||||
|
"TERM": "xterm",
|
||||||
|
"LANG": "C",
|
||||||
|
"LC_CTYPE": "en_US.UTF-8",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# environ for py files has a few changes.
|
||||||
|
pyenviron = os.environ.copy()
|
||||||
|
pyenviron.update(
|
||||||
|
{
|
||||||
|
"PYTHONPATH": str(script_path),
|
||||||
|
"fish": str(fishdir / "fish"),
|
||||||
|
"fish_key_reader": str(fishdir / "fish_key_reader"),
|
||||||
|
"fish_indent": str(fishdir / "fish_indent"),
|
||||||
|
"TERM": "dumb",
|
||||||
|
"FISH_FORCE_COLOR": "1" if sys.stdout.isatty() else "0",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
os.chdir(home.name)
|
||||||
|
|
||||||
|
print(f"Checking files (TMPDIR is {home.name})")
|
||||||
|
if not PEXPECT and any(x.endswith(".py") for (x, _) in files):
|
||||||
|
print(f"{RED}Skipping pexpect tests because pexpect is not installed{RESET}")
|
||||||
|
|
||||||
|
for f, arg in files:
|
||||||
|
starttime = datetime.now()
|
||||||
|
if f.endswith(".fish"):
|
||||||
|
subs = def_subs.copy()
|
||||||
|
subs["s"] = f
|
||||||
|
# littlecheck
|
||||||
|
print(f"{arg}..", end="", flush=True)
|
||||||
|
ret = littlecheck.check_path(f, subs, lconfig, lambda x: print(x.message()))
|
||||||
|
endtime = datetime.now()
|
||||||
|
duration_ms = round((endtime - starttime).total_seconds() * 1000)
|
||||||
|
if ret is littlecheck.SKIP:
|
||||||
|
print(f"{BLUE}SKIPPED{RESET}")
|
||||||
|
skipcount += 1
|
||||||
|
elif ret:
|
||||||
|
print(f"{GREEN}PASS{RESET} ({duration_ms} ms)")
|
||||||
|
passcount += 1
|
||||||
|
else:
|
||||||
|
print(f"{RED}FAIL{RESET} ({duration_ms} ms)")
|
||||||
|
failcount += 1
|
||||||
|
elif f.endswith(".py"):
|
||||||
|
print(f"{arg}..", end="", flush=True)
|
||||||
|
if not PEXPECT:
|
||||||
|
print(f"{BLUE}SKIPPED{RESET}")
|
||||||
|
skipcount += 1
|
||||||
|
continue
|
||||||
|
proc = subprocess.run(
|
||||||
|
["python3", f],
|
||||||
|
capture_output=True,
|
||||||
|
env=pyenviron,
|
||||||
|
)
|
||||||
|
endtime = datetime.now()
|
||||||
|
duration_ms = round((endtime - starttime).total_seconds() * 1000)
|
||||||
|
if proc.returncode == 0:
|
||||||
|
print(f"{GREEN}PASS{RESET} ({duration_ms} ms)")
|
||||||
|
passcount += 1
|
||||||
|
elif proc.returncode == 127:
|
||||||
|
print(f"{BLUE}SKIPPED{RESET}")
|
||||||
|
skipcount += 1
|
||||||
|
else:
|
||||||
|
print(f"{RED}FAILED{RESET} ({duration_ms} ms)")
|
||||||
|
if proc.stdout:
|
||||||
|
print(proc.stdout.decode("utf-8"))
|
||||||
|
if proc.stderr:
|
||||||
|
print(proc.stderr.decode("utf-8"))
|
||||||
|
failcount += 1
|
||||||
|
else:
|
||||||
|
print(f"Not a valid test file: {arg}")
|
||||||
|
failcount += 1
|
||||||
|
if passcount + failcount + skipcount > 1:
|
||||||
|
print(f"{passcount} / {passcount + failcount} passed ({skipcount} skipped)")
|
||||||
|
if passcount == 0 and failcount == 0 and skipcount:
|
||||||
|
return 125
|
||||||
|
return 1 if failcount else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
ret = main()
|
||||||
|
sys.exit(ret)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(130)
|
Loading…
Reference in a new issue