Merge branch 'ctest'

This commit is contained in:
Mahmoud Al-Qudsi 2021-08-29 13:03:16 -05:00
commit 069d396ebc
19 changed files with 504 additions and 409 deletions

View File

@ -2,6 +2,9 @@ name: C/C++ CI
on: [push, pull_request]
env:
CTEST_PARALLEL_LEVEL: "1"
jobs:
ubuntu:

View File

@ -6,7 +6,6 @@ from __future__ import unicode_literals
from __future__ import print_function
import argparse
from collections import deque
import datetime
import io
import re
@ -468,7 +467,6 @@ class TestRun(object):
"""
return [s + "\n" for s in s.decode("utf-8").split("\n")]
PIPE = subprocess.PIPE
if self.config.verbose:
print(self.subbed_command)
proc = runproc(self.subbed_command)
@ -593,13 +591,13 @@ class Checker(object):
# Find run commands.
self.runcmds = [RunCmd.parse(sl) for sl in group1s(RUN_RE)]
self.shebang_cmd = None
if not self.runcmds:
# If no RUN command has been given, fall back to the shebang.
if lines[0].text.startswith("#!"):
# Remove the "#!" at the beginning, and the newline at the end.
cmd = lines[0].text[2:-1]
if not find_command(cmd):
raise CheckerError("Command could not be found: " + cmd)
self.shebang_cmd = cmd
self.runcmds = [RunCmd(cmd + " %s", lines[0])]
else:
raise CheckerError("No runlines ('# RUN') found")
@ -627,11 +625,13 @@ def check_file(input_file, name, subs, config, failure_handler):
proc = runproc(
perform_substitution(reqcmd.args, subs)
)
stdout, stderr = proc.communicate()
status = proc.returncode
proc.communicate()
if proc.returncode > 0:
return SKIP
if checker.shebang_cmd is not None and not find_command(checker.shebang_cmd):
raise CheckerError("Command could not be found: " + checker.shebang_cmd)
# Only then run the RUN lines.
for runcmd in checker.runcmds:
failure = TestRun(name, runcmd, checker, subs, config).run()
@ -698,13 +698,16 @@ def main():
def_subs = {"%": "%"}
def_subs.update(parse_subs(args.substitute))
failure_count = 0
tests_count = 0
failed = False
skip_count = 0
config = Config()
config.colorize = sys.stdout.isatty()
config.progress = args.progress
fields = config.colors()
for path in args.file:
tests_count += 1
fields["path"] = path
if config.progress:
print("Testing file {path} ... ".format(**fields), end="")
@ -714,13 +717,14 @@ def main():
starttime = datetime.datetime.now()
ret = check_path(path, subs, config, TestFailure.print_message)
if not ret:
failure_count += 1
failed = True
elif config.progress:
endtime = datetime.datetime.now()
duration_ms = round((endtime - starttime).total_seconds() * 1000)
reason = "ok"
color = "{GREEN}"
if ret is SKIP:
skip_count += 1
reason = "SKIPPED"
color = "{BLUE}"
print(
@ -728,7 +732,15 @@ def main():
duration=duration_ms, reason=reason, **fields
)
)
sys.exit(failure_count)
# To facilitate integration with testing frameworks, use exit code 125 to indicate that all
# tests have been skipped (primarily for use when tests are run one at a time). Exit code 125 is
# used to indicate to automated `git bisect` runs that a revision has been skipped; we use it
# for the same reasons git does.
if skip_count > 0 and skip_count == tests_count:
sys.exit(125)
sys.exit(1 if failed else 0)
if __name__ == "__main__":

View File

@ -175,8 +175,9 @@ install(FILES fish.png DESTINATION ${rel_datadir}/pixmaps)
# Group install targets into a InstallTargets folder
set_property(TARGET build_fish_pc CHECK-FISH-BUILD-VERSION-FILE
test_fishscript
test_prep tests_buildroot_target
# test_fishscript
# test_prep
tests_buildroot_target
PROPERTY FOLDER cmake/InstallTargets)
# Make a target build_root that installs into the buildroot directory, for testing.

View File

@ -1,4 +1,48 @@
# Define fish_tests.
# This adds ctest support to the project
enable_testing()
# By default, ctest runs tests serially
if(NOT CTEST_PARALLEL_LEVEL)
include(ProcessorCount)
ProcessorCount(CORES)
set(CTEST_PARALLEL_LEVEL ${CORES})
endif()
# We will use 125 as a reserved exit code to indicate that a test has been skipped, i.e. it did not
# pass but it should not be considered a failed test run, either.
set(SKIP_RETURN_CODE 125)
# Even though we are using CMake's ctest for testing, we still define our own `make test` target
# rather than use its default for many reasons:
# * CMake doesn't run tests in-proc or even add each tests as an individual node in the ninja
# dependency tree, instead it just bundles all tests into a target called `test` that always just
# shells out to `ctest`, so there are no build-related benefits to not doing that ourselves.
# * CMake devs insist that it is appropriate for `make test` to never depend on `make all`, i.e.
# running `make test` does not require any of the binaries to be built before testing.
# * The only way to have a test depend on a binary is to add a fake test with a name like
# "build_fish" that executes CMake recursively to build the `fish` target.
# * It is not possible to set top-level CTest options/settings such as CTEST_PARALLEL_LEVEL from
# within the CMake configuration file.
# * Circling back to the point about individual tests not being actual Makefile targets, CMake does
# not offer any way to execute a named test via the `make`/`ninja`/whatever interface; the only
# way to manually invoke test `foo` is to to manually run `ctest` and specify a regex matching
# `foo` as an argument, e.g. `ctest -R ^foo$`... which is really crazy.
# Set a policy so CMake stops complaining when we use the target name "test"
cmake_policy(PUSH)
if(POLICY CMP0037)
cmake_policy(SET CMP0037 OLD)
endif()
add_custom_target(test
COMMAND env CTEST_PARALLEL_LEVEL=${CTEST_PARALLEL_LEVEL}
${CMAKE_CTEST_COMMAND} --force-new-ctest-process
--output-on-failure
DEPENDS fish_tests tests_buildroot_target
USES_TERMINAL
)
cmake_policy(POP)
# Build the low-level tests code
add_executable(fish_tests EXCLUDE_FROM_ALL
src/fish_tests.cpp)
fish_link_deps_and_sign(fish_tests)
@ -6,24 +50,41 @@ fish_link_deps_and_sign(fish_tests)
# The "test" directory.
set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test)
# CMake doesn't really support dynamic test discovery where a test harness is executed to list the
# tests it contains, making fish_tests.cpp's tests opaque to CMake (whereas littlecheck tests can be
# enumerated from the filesystem). We used to compile fish_tests.cpp without linking against
# anything (-Wl,-undefined,dynamic_lookup,--unresolved-symbols=ignore-all) to get it to print its
# tests at configuration time, but that's a little too much dark CMake magic.
#
# We now identify tests by checking against a magic regex that's #define'd as a no-op C-side.
file(READ "${CMAKE_SOURCE_DIR}/src/fish_tests.cpp" FISH_TESTS_CPP)
string(REGEX MATCHALL "TEST_GROUP\\( *\"([^\"]+)\"" "LOW_LEVEL_TESTS" "${FISH_TESTS_CPP}")
string(REGEX REPLACE "TEST_GROUP\\( *\"([^\"]+)\"" "\\1" "LOW_LEVEL_TESTS" "${LOW_LEVEL_TESTS}")
list(REMOVE_DUPLICATES LOW_LEVEL_TESTS)
# The directory into which fish is installed.
set(TEST_INSTALL_DIR ${TEST_DIR}/buildroot)
# The directory where the tests expect to find the fish root (./bin, etc)
set(TEST_ROOT_DIR ${TEST_DIR}/root)
# Copy tests files.
file(GLOB TESTS_FILES tests/*)
add_custom_target(tests_dir DEPENDS tests)
# Copy needed directories for out-of-tree builds
if(NOT FISH_IN_TREE_BUILD)
add_custom_command(TARGET tests_dir
add_custom_target(funcs_dir)
add_custom_command(TARGET funcs_dir
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/share && ln -sf
${CMAKE_SOURCE_DIR}/share/functions/ ${CMAKE_BINARY_DIR}/share/functions
COMMENT "Symlinking fish functions to binary dir"
VERBATIM)
add_custom_target(tests_dir DEPENDS tests)
add_custom_command(TARGET tests_dir
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/tests/ ${CMAKE_BINARY_DIR}/tests/
COMMENT "Copying test files to binary dir"
VERBATIM)
add_dependencies(fish_tests tests_dir)
add_dependencies(fish_tests tests_dir funcs_dir)
endif()
# Copy littlecheck.py
@ -32,119 +93,63 @@ configure_file(build_tools/littlecheck.py littlecheck.py COPYONLY)
# Copy pexpect_helper.py
configure_file(build_tools/pexpect_helper.py pexpect_helper.py COPYONLY)
# Make the directory in which to run tests.
# Also symlink fish to where the tests expect it to be.
# Lastly put fish_test_helper there too.
# CMake being CMake, you can't just add a DEPENDS argument to add_test to make it depend on any of
# your binaries actually being built before `make test` is executed (requiring `make all` first),
# and the only dependency a test can have is on another test. So we make building fish and
# `fish_tests` prerequisites to our entire top-level `test` target.
function(add_test_target NAME)
add_custom_target("test_${NAME}"
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R "^${NAME}$$"
DEPENDS fish_tests tests_buildroot_target
USES_TERMINAL
)
endfunction()
add_custom_target(tests_buildroot_target
# Make the directory in which to run tests:
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_INSTALL_DIR}
COMMAND DESTDIR=${TEST_INSTALL_DIR} ${CMAKE_COMMAND}
--build ${CMAKE_CURRENT_BINARY_DIR} --target install
# Put fish_test_helper there too:
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/fish_test_helper
${TEST_INSTALL_DIR}/${CMAKE_INSTALL_PREFIX}/bin
# Also symlink fish to where the tests expect it to be:
COMMAND ${CMAKE_COMMAND} -E create_symlink
${TEST_INSTALL_DIR}/${CMAKE_INSTALL_PREFIX}
${TEST_ROOT_DIR}
DEPENDS fish fish_test_helper)
if(NOT FISH_IN_TREE_BUILD)
# We need to symlink share/functions for the tests.
# This should be simplified.
add_custom_target(symlink_functions
COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_CURRENT_SOURCE_DIR}/share/functions
${CMAKE_CURRENT_BINARY_DIR}/share/functions)
add_dependencies(tests_buildroot_target symlink_functions)
else()
add_custom_target(symlink_functions)
endif()
# Prep the environment for running the unit tests.
add_custom_target(test_prep
# Add directories hard-coded into the tests
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEST_DIR}/data
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_DIR}/data
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEST_DIR}/temp
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_DIR}/temp
# Add the XDG_* directories
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEST_DIR}/xdg_data
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_DIR}/xdg_data
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEST_DIR}/xdg_config
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_DIR}/xdg_config
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEST_DIR}/xdg_runtime
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_DIR}/xdg_runtime
DEPENDS tests_buildroot_target tests_dir
USES_TERMINAL)
# Define our individual tests.
# Each test is conceptually independent.
# However when running all tests, we want to run them serially for sanity's sake.
# So define both a normal target, and a serial variant which enforces ordering.
foreach(TESTTYPE test serial_test)
add_custom_target(${TESTTYPE}_low_level
COMMAND env XDG_DATA_DIRS=
XDG_DATA_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_data
XDG_CONFIG_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_config
XDG_RUNTIME_DIR=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_runtime
./fish_tests
foreach(LTEST ${LOW_LEVEL_TESTS})
add_test(
NAME ${LTEST}
COMMAND ${CMAKE_BINARY_DIR}/fish_tests ${LTEST}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS fish_tests
USES_TERMINAL)
)
set_tests_properties(${LTEST} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
add_test_target("${LTEST}")
endforeach(LTEST)
add_custom_target(${TESTTYPE}_fishscript
COMMAND
cd tests &&
env XDG_DATA_DIRS=
XDG_DATA_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_data
XDG_CONFIG_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_config
XDG_RUNTIME_DIR=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_runtime
${TEST_ROOT_DIR}/bin/fish test.fish
DEPENDS test_prep
USES_TERMINAL)
FILE(GLOB FISH_CHECKS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/checks/*.fish)
foreach(CHECK ${FISH_CHECKS})
get_filename_component(CHECK_NAME ${CHECK} NAME)
get_filename_component(CHECK ${CHECK} NAME_WE)
add_test(NAME ${CHECK_NAME}
COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
${CMAKE_CURRENT_BINARY_DIR}/tests/test.fish ${CHECK}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
)
set_tests_properties(${CHECK_NAME} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
add_test_target("${CHECK_NAME}")
endforeach(CHECK)
add_custom_target(${TESTTYPE}_interactive
COMMAND cd tests &&
env XDG_DATA_DIRS=
XDG_DATA_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_data
XDG_CONFIG_HOME=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_config
XDG_RUNTIME_DIR=${CMAKE_CURRENT_BINARY_DIR}/test/xdg_runtime
${TEST_ROOT_DIR}/bin/fish interactive.fish
DEPENDS test_prep
USES_TERMINAL)
endforeach(TESTTYPE)
# Now add a dependency chain between the serial versions.
# This ensures they run in order.
add_dependencies(serial_test_low_level test_prep)
add_dependencies(serial_test_fishscript serial_test_low_level)
add_dependencies(serial_test_interactive serial_test_fishscript)
add_custom_target(serial_test_high_level
DEPENDS serial_test_interactive serial_test_fishscript)
# Create the 'test' target.
# Set a policy so CMake stops complaining about the name 'test'.
cmake_policy(PUSH)
if(${CMAKE_VERSION} VERSION_LESS 3.11.0 AND POLICY CMP0037)
cmake_policy(SET CMP0037 OLD)
endif()
add_custom_target(test)
cmake_policy(POP)
add_dependencies(test serial_test_high_level)
# Group test targets into a TestTargets folder
set_property(TARGET test tests_dir
test_low_level
test_fishscript
test_interactive
test_fishscript test_prep
tests_buildroot_target
serial_test_high_level
serial_test_low_level
serial_test_fishscript
serial_test_interactive
symlink_functions
PROPERTY FOLDER cmake/TestTargets)
FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
foreach(PEXPECT ${PEXPECTS})
get_filename_component(PEXPECT ${PEXPECT} NAME)
add_test(NAME ${PEXPECT}
COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
${CMAKE_CURRENT_BINARY_DIR}/tests/interactive.fish ${PEXPECT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
)
set_tests_properties(${PEXPECT} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
add_test_target("${PEXPECT}")
endforeach(PEXPECT)

View File

@ -103,8 +103,7 @@ static bool should_test_function(const char *func_name, bool default_on = true)
result = default_on;
} else {
for (size_t i = 0; s_arguments[i] != nullptr; i++) {
if (!std::strncmp(func_name, s_arguments[i], std::strlen(s_arguments[i]))) {
// Prefix match.
if (!std::strcmp(func_name, s_arguments[i])) {
result = true;
break;
}
@ -4341,6 +4340,11 @@ void history_tests_t::test_history_races_pound_on_history(size_t item_count, siz
}
void history_tests_t::test_history_races() {
// This always fails under WSL
if (is_windows_subsystem_for_linux()) {
return;
}
say(L"Testing history race conditions");
// It appears TSAN and ASAN's allocators do not release their locks properly in atfork, so
@ -6647,11 +6651,138 @@ void termsize_tester_t::test() {
do_test(ts2.updating(parser) == *stubby_termsize);
}
// typedef void (test_entry_point_t)();
using test_entry_point_t = void (*)();
struct test_t {
const char *group;
std::function<void()> test;
bool opt_in = false;
test_t(const char *group, test_entry_point_t test, bool opt_in = false)
: group(group), test(test), opt_in(opt_in) {}
};
struct test_comparator_t {
// template<typename T=test_t>
int operator()(const test_t &lhs, const test_t &rhs) { return strcmp(lhs.group, rhs.group); }
};
// This magic string is required for CMake to pick up the list of tests
#define TEST_GROUP(x) x
static const test_t s_tests[]{
{TEST_GROUP("utility_functions"), test_utility_functions},
{TEST_GROUP("string_split"), test_split_string_tok},
{TEST_GROUP("wwrite_to_fd"), test_wwrite_to_fd},
{TEST_GROUP("env_vars"), test_env_vars},
{TEST_GROUP("env"), test_env_snapshot},
{TEST_GROUP("str_to_num"), test_str_to_num},
{TEST_GROUP("enum"), test_enum_set},
{TEST_GROUP("enum"), test_enum_array},
{TEST_GROUP("highlighting"), test_highlighting},
{TEST_GROUP("new_parser_ll2"), test_new_parser_ll2},
{TEST_GROUP("new_parser_fuzzing"), test_new_parser_fuzzing},
{TEST_GROUP("new_parser_correctness"), test_new_parser_correctness},
{TEST_GROUP("new_parser_ad_hoc"), test_new_parser_ad_hoc},
{TEST_GROUP("new_parser_errors"), test_new_parser_errors},
{TEST_GROUP("error_messages"), test_error_messages},
{TEST_GROUP("escape"), test_unescape_sane},
{TEST_GROUP("escape"), test_escape_crazy},
{TEST_GROUP("escape"), test_escape_quotes},
{TEST_GROUP("format"), test_format},
{TEST_GROUP("convert"), test_convert},
{TEST_GROUP("convert"), test_convert_private_use},
{TEST_GROUP("convert_ascii"), test_convert_ascii},
{TEST_GROUP("perf_convert_ascii"), perf_convert_ascii, true},
{TEST_GROUP("convert_nulls"), test_convert_nulls},
{TEST_GROUP("tokenizer"), test_tokenizer},
{TEST_GROUP("fd_monitor"), test_fd_monitor},
{TEST_GROUP("iothread"), test_iothread},
{TEST_GROUP("pthread"), test_pthread},
{TEST_GROUP("debounce"), test_debounce},
{TEST_GROUP("debounce"), test_debounce_timeout},
{TEST_GROUP("parser"), test_parser},
{TEST_GROUP("cancellation"), test_cancellation},
{TEST_GROUP("indents"), test_indents},
{TEST_GROUP("utf8"), test_utf8},
{TEST_GROUP("feature_flags"), test_feature_flags},
{TEST_GROUP("escape_sequences"), test_escape_sequences},
{TEST_GROUP("pcre2_escape"), test_pcre2_escape},
{TEST_GROUP("lru"), test_lru},
{TEST_GROUP("expand"), test_expand},
{TEST_GROUP("expand"), test_expand_overflow},
{TEST_GROUP("fuzzy_match"), test_fuzzy_match},
{TEST_GROUP("ifind"), test_ifind},
{TEST_GROUP("ifind_fuzzy"), test_ifind_fuzzy},
{TEST_GROUP("abbreviations"), test_abbreviations},
{TEST_GROUP("builtin_test"), test_test},
{TEST_GROUP("wcstod"), test_wcstod},
{TEST_GROUP("dup2s"), test_dup2s},
{TEST_GROUP("dup2s"), test_dup2s_fd_for_target_fd},
{TEST_GROUP("path"), test_path},
{TEST_GROUP("pager_navigation"), test_pager_navigation},
{TEST_GROUP("pager_layout"), test_pager_layout},
{TEST_GROUP("word_motion"), test_word_motion},
{TEST_GROUP("is_potential_path"), test_is_potential_path},
{TEST_GROUP("colors"), test_colors},
{TEST_GROUP("complete"), test_complete},
{TEST_GROUP("autoload"), test_autoload},
{TEST_GROUP("input"), test_input},
{TEST_GROUP("line_iterator"), test_line_iterator},
{TEST_GROUP("undo"), test_undo},
{TEST_GROUP("universal"), test_universal},
{TEST_GROUP("universal"), test_universal_output},
{TEST_GROUP("universal"), test_universal_parsing},
{TEST_GROUP("universal"), test_universal_parsing_legacy},
{TEST_GROUP("universal"), test_universal_callbacks},
{TEST_GROUP("universal"), test_universal_formats},
{TEST_GROUP("universal"), test_universal_ok_to_save},
{TEST_GROUP("notifiers"), test_universal_notifiers},
{TEST_GROUP("wait_handles"), test_wait_handles},
{TEST_GROUP("completion_insertions"), test_completion_insertions},
{TEST_GROUP("autosuggestion_ignores"), test_autosuggestion_ignores},
{TEST_GROUP("autosuggestion_combining"), test_autosuggestion_combining},
{TEST_GROUP("autosuggest_suggest_special"), test_autosuggest_suggest_special},
{TEST_GROUP("history"), history_tests_t::test_history},
{TEST_GROUP("history_merge"), history_tests_t::test_history_merge},
{TEST_GROUP("history_paths"), history_tests_t::test_history_path_detection},
{TEST_GROUP("history_races"), history_tests_t::test_history_races},
{TEST_GROUP("history_formats"), history_tests_t::test_history_formats},
{TEST_GROUP("string"), test_string},
{TEST_GROUP("illegal_command_exit_code"), test_illegal_command_exit_code},
{TEST_GROUP("maybe"), test_maybe},
{TEST_GROUP("layout_cache"), test_layout_cache},
{TEST_GROUP("prompt"), test_prompt_truncation},
{TEST_GROUP("normalize"), test_normalize_path},
{TEST_GROUP("dirname"), test_dirname_basename},
{TEST_GROUP("topics"), test_topic_monitor},
{TEST_GROUP("topics"), test_topic_monitor_torture},
{TEST_GROUP("pipes"), test_pipes},
{TEST_GROUP("fd_event"), test_fd_event_signaller},
{TEST_GROUP("timer_format"), test_timer_format},
{TEST_GROUP("termsize"), termsize_tester_t::test},
{TEST_GROUP("killring"), test_killring},
};
void list_tests() {
std::set<std::string> groups;
for (const auto &test : s_tests) {
groups.insert(test.group);
}
for (const auto &group : groups) {
std::fprintf(stdout, "%s\n", group.c_str());
}
}
/// Main test.
int main(int argc, char **argv) {
UNUSED(argc);
setlocale(LC_ALL, "");
if (argc >= 2 && std::strcmp(argv[1], "--list") == 0) {
list_tests();
return 0;
}
// Look for the file tests/test.fish. We expect to run in a directory containing that file.
// If we don't find it, walk up the directory hierarchy until we do, or error.
while (access("./tests/test.fish", F_OK) != 0) {
@ -6696,103 +6827,11 @@ int main(int argc, char **argv) {
// Set PWD from getcwd - fixes #5599
env_stack_t::principal().set_pwd_from_getcwd();
if (should_test_function("utility_functions")) test_utility_functions();
if (should_test_function("string_split")) test_split_string_tok();
if (should_test_function("wwrite_to_fd")) test_wwrite_to_fd();
if (should_test_function("env_vars")) test_env_vars();
if (should_test_function("env")) test_env_snapshot();
if (should_test_function("str_to_num")) test_str_to_num();
if (should_test_function("enum")) test_enum_set();
if (should_test_function("enum")) test_enum_array();
if (should_test_function("highlighting")) test_highlighting();
if (should_test_function("new_parser_ll2")) test_new_parser_ll2();
if (should_test_function("new_parser_fuzzing"))
test_new_parser_fuzzing(); // fuzzing is expensive
if (should_test_function("new_parser_correctness")) test_new_parser_correctness();
if (should_test_function("new_parser_ad_hoc")) test_new_parser_ad_hoc();
if (should_test_function("new_parser_errors")) test_new_parser_errors();
if (should_test_function("error_messages")) test_error_messages();
if (should_test_function("escape")) test_unescape_sane();
if (should_test_function("escape")) test_escape_crazy();
if (should_test_function("escape")) test_escape_quotes();
if (should_test_function("format")) test_format();
if (should_test_function("convert")) test_convert();
if (should_test_function("convert")) test_convert_private_use();
if (should_test_function("convert_ascii")) test_convert_ascii();
if (should_test_function("perf_convert_ascii", false)) perf_convert_ascii();
if (should_test_function("convert_nulls")) test_convert_nulls();
if (should_test_function("tokenizer")) test_tokenizer();
if (should_test_function("fd_monitor")) test_fd_monitor();
if (should_test_function("iothread")) test_iothread();
if (should_test_function("pthread")) test_pthread();
if (should_test_function("debounce")) test_debounce();
if (should_test_function("debounce")) test_debounce_timeout();
if (should_test_function("parser")) test_parser();
if (should_test_function("cancellation")) test_cancellation();
if (should_test_function("indents")) test_indents();
if (should_test_function("utf8")) test_utf8();
if (should_test_function("feature_flags")) test_feature_flags();
if (should_test_function("escape_sequences")) test_escape_sequences();
if (should_test_function("pcre2_escape")) test_pcre2_escape();
if (should_test_function("lru")) test_lru();
if (should_test_function("expand")) test_expand();
if (should_test_function("expand")) test_expand_overflow();
if (should_test_function("fuzzy_match")) test_fuzzy_match();
if (should_test_function("ifind")) test_ifind();
if (should_test_function("ifind_fuzzy")) test_ifind_fuzzy();
if (should_test_function("abbreviations")) test_abbreviations();
if (should_test_function("test")) test_test();
if (should_test_function("wcstod")) test_wcstod();
if (should_test_function("dup2s")) test_dup2s();
if (should_test_function("dup2s")) test_dup2s_fd_for_target_fd();
if (should_test_function("path")) test_path();
if (should_test_function("pager_navigation")) test_pager_navigation();
if (should_test_function("pager_layout")) test_pager_layout();
if (should_test_function("word_motion")) test_word_motion();
if (should_test_function("is_potential_path")) test_is_potential_path();
if (should_test_function("colors")) test_colors();
if (should_test_function("complete")) test_complete();
if (should_test_function("autoload")) test_autoload();
if (should_test_function("input")) test_input();
if (should_test_function("line_iterator")) test_line_iterator();
if (should_test_function("undo")) test_undo();
if (should_test_function("universal")) test_universal();
if (should_test_function("universal")) test_universal_output();
if (should_test_function("universal")) test_universal_parsing();
if (should_test_function("universal")) test_universal_parsing_legacy();
if (should_test_function("universal")) test_universal_callbacks();
if (should_test_function("universal")) test_universal_formats();
if (should_test_function("universal")) test_universal_ok_to_save();
if (should_test_function("notifiers")) test_universal_notifiers();
if (should_test_function("wait_handles")) test_wait_handles();
if (should_test_function("completion_insertions")) test_completion_insertions();
if (should_test_function("autosuggestion_ignores")) test_autosuggestion_ignores();
if (should_test_function("autosuggestion_combining")) test_autosuggestion_combining();
if (should_test_function("autosuggest_suggest_special")) test_autosuggest_suggest_special();
if (should_test_function("history")) history_tests_t::test_history();
if (should_test_function("history_merge")) history_tests_t::test_history_merge();
if (should_test_function("history_paths")) history_tests_t::test_history_path_detection();
if (!is_windows_subsystem_for_linux()) {
// this test always fails under WSL
if (should_test_function("history_races")) history_tests_t::test_history_races();
for (const auto &test : s_tests) {
if (should_test_function(test.group)) {
test.test();
}
}
if (should_test_function("history_formats")) history_tests_t::test_history_formats();
if (should_test_function("string")) test_string();
if (should_test_function("illegal_command_exit_code")) test_illegal_command_exit_code();
if (should_test_function("maybe")) test_maybe();
if (should_test_function("layout_cache")) test_layout_cache();
if (should_test_function("prompt")) test_prompt_truncation();
if (should_test_function("normalize")) test_normalize_path();
if (should_test_function("dirname")) test_dirname_basename();
if (should_test_function("topics")) test_topic_monitor();
if (should_test_function("topics")) test_topic_monitor_torture();
if (should_test_function("pipes")) test_pipes();
if (should_test_function("fd_event")) test_fd_event_signaller();
if (should_test_function("timer_format")) test_timer_format();
// history_tests_t::test_history_speed();
if (should_test_function("termsize")) termsize_tester_t::test();
if (should_test_function("killring")) test_killring();
say(L"Encountered %d errors in low-level tests", err_count);
if (s_test_run_count == 0) say(L"*** No Tests Were Actually Run! ***");

View File

@ -3,6 +3,10 @@
# Test function, loops, conditionals and some basic elements
#
# The test driver always starts each test in its own temporary directory, but to make it easier to
# run this test directly for whatever reason:
set -g tmpdir (mktemp -d)
# Comments in odd places don't cause problems
for i in 1 2 # Comment on same line as command
# Comment inside loop
@ -42,12 +46,12 @@ end
# Simple function tests
function foo
echo >../test/temp/fish_foo.txt $argv
echo > $tmpdir/fish_foo.txt $argv
end
foo hello
cat ../test/temp/fish_foo.txt |read foo
cat $tmpdir/fish_foo.txt |read foo
if test $foo = hello;
echo Test 2 pass
@ -297,7 +301,7 @@ else if test -n "def"
else if not_a_valid_command but it should be OK because a previous branch was taken
echo "epsilon 5.3"
else if test ! -n "abc"
echo "epsilon 5.4"
echo "epsilon 5.4"
end
#CHECK: epsilon5.2
@ -327,10 +331,10 @@ type -q -f fish_test_type_zzz ; echo $status
# ensure that builtins that produce no output can still truncate files
# (bug PCA almost reintroduced!)
echo abc > ../test/temp/file_truncation_test.txt
cat ../test/temp/file_truncation_test.txt
echo -n > ../test/temp/file_truncation_test.txt
cat ../test/temp/file_truncation_test.txt
echo abc > $tmpdir/file_truncation_test.txt
cat $tmpdir/file_truncation_test.txt
echo -n > $tmpdir/file_truncation_test.txt
cat $tmpdir/file_truncation_test.txt
#CHECK: abc
# Test events.

View File

@ -8,14 +8,14 @@ complete -c complete_test_alpha2 --no-files -w 'complete_test_alpha1 extra1'
complete -c complete_test_alpha3 --no-files -w 'complete_test_alpha2 extra2'
complete -C'complete_test_alpha1 arg1 '
# CHECK: complete_test_alpha1 arg1
# CHECK: complete_test_alpha1 arg1
complete -C'complete_test_alpha2 arg2 '
# CHECK: complete_test_alpha1 extra1 arg2
# CHECK: complete_test_alpha1 extra1 arg2
complete -C'complete_test_alpha3 arg3 '
# CHECK: complete_test_alpha1 extra1 extra2 arg3
# CHECK: complete_test_alpha1 extra1 extra2 arg3
# Works even with the argument as a separate token.
complete -C 'complete_test_alpha3 arg3 '
# CHECK: complete_test_alpha1 extra1 extra2 arg3
# CHECK: complete_test_alpha1 extra1 extra2 arg3
alias myalias1 'complete_test_alpha1 arg1'
alias myalias2='complete_test_alpha1 arg2'
@ -26,8 +26,8 @@ myalias2 call2
# CHECK: arg2 call2
complete -C'myalias1 call3 '
complete -C'myalias2 call3 '
# CHECK: complete_test_alpha1 arg1 call3
# CHECK: complete_test_alpha1 arg2 call3
# CHECK: complete_test_alpha1 arg1 call3
# CHECK: complete_test_alpha1 arg2 call3
# Ensure that commands can't wrap themselves - if this did,
# the completion would be executed a bunch of times.
@ -199,7 +199,7 @@ complete -C'BBBB -'
echo "BBBB:"
complete -C'BBBB -'
#CHECK: BBBB:
#CHECK:
#CHECK:
# Test that erasing completions works correctly
echo
@ -290,7 +290,11 @@ if begin
rm -rf test6.tmp.dir; and mkdir test6.tmp.dir
end
pushd test6.tmp.dir
set -l dir (mktemp -d XXXXXXXX)
# The "incorrect implicit cd from PATH" fails if mktemp returns an absolute path and
# `realpath --relative-to` is not available on macOS.
# set dir (realpath --relative-to="$PWD" (mktemp -d XXXXXXXX))
set dir (basename (mktemp -d XXXXXXXX))
mkdir -p $dir
if complete -C$dir | grep "^$dir/.*Directory" >/dev/null
echo "implicit cd complete works"
else

View File

@ -1,2 +1,3 @@
#RUN: env HOME="$(mktemp -d)" XDG_CONFIG_HOME="$(mktemp -d)" %fish --features '' -c 'string match --quiet "??" ab ; echo "qmarkon: $status"'
# Explicitly overriding HOME/XDG_CONFIG_HOME is only required if not invoking via `make test`
# RUN: env HOME="$(mktemp -d)" XDG_CONFIG_HOME="$(mktemp -d)" %fish --features '' -c 'string match --quiet "??" ab ; echo "qmarkon: $status"'
#CHECK: qmarkon: 0

View File

@ -1,22 +1,5 @@
#RUN: %fish %s
# Ensure there's no zombies before we start, otherwise the tests will mysteriously fail.
set -l zombies_among_us
if not contains (uname) SunOS
set zombies_among_us (ps -o stat | string match 'Z*' | count)
else
# Solaris' ps is awkward, I don't know if this actually works
set zombies_among_us (ps -o s | string match 'Z*' | count)
end
[ "$zombies_among_us" -eq "0" ]
or begin
echo "Found existing zombie processes. Clean up zombies before running this test."
exit 1
end
echo "All clear of zombies."
# CHECK: All clear of zombies.
# Verify zombies are not left by disown (#7183, #5342)
# Do this first to avoid colliding with the other disowned processes below, which may
# still be running at the end of the script
@ -28,13 +11,15 @@ sleep 0.1
#CHECK: Trigger process reaping
# The initial approach here was to kill the PID of the sleep process, which should
# be gone by the time we get here. Unfortunately, kill from procps on pre-2016 distributions
# does not print an error for non-existent PIDs, so instead look for zombies in this session
# (there should be none).
# does not print an error for non-existent PIDs, so instead look for zombies in this session.
# There could already be zombies from previous tests run in this session or a test could be run
# simultaneously that causes a zombie to spawn, so limit the output only to processes started by
# this fish instance.
if not contains (uname) SunOS
ps -o stat | string match 'Z*'
ps -o ppid,stat
else
ps -o s | string match 'Z*'
end
ps -o ppid,s
end | string match -e $fish_pid | string match 'Z*'
# Verify disown can be used with last_pid, even if it is separate from the pgroup.
# This should silently succeed.

View File

@ -60,15 +60,16 @@ foo $tmpdir/bar
cat $tmpdir/bar
# CHECK: foo
rm -Rf $tmpdir
# Verify that we can turn stderr into stdout and then pipe it
# Note that the order here has historically been unspecified - 'errput' could conceivably appear before 'output'.
begin
echo output
echo errput 1>&2
end 2>&1 | sort | tee ../test/temp/tee_test.txt
cat ../test/temp/tee_test.txt
end 2>&1 | sort | tee $tmpdir/tee_test.txt
cat $tmpdir/tee_test.txt
rm -Rf $tmpdir
#CHECK: errput
#CHECK: output
#CHECK: errput

View File

@ -1,4 +1,5 @@
# RUN: env XDG_CONFIG_HOME="$(mktemp -d)" FISH=%fish %fish %s
# Explicitly overriding HOME/XDG_CONFIG_HOME is only required if not invoking via `make test`
# RUN: env FISH=%fish HOME="$(mktemp -d)" XDG_CONFIG_HOME="$(mktemp -d)" %fish %s
# Environment variable tests
# Test if variables can be properly set
@ -476,7 +477,7 @@ echo "global-vs-universal 4: $__fish_test_global_vs_universal"
set -e -U __fish_test_global_vs_universal
echo "global-vs-universal 5: $__fish_test_global_vs_universal"
# CHECK: global-vs-universal 5:
# CHECK: global-vs-universal 5:
# Export local variables from all parent scopes (issue #6153).
function func
@ -711,14 +712,14 @@ true
set "" foo
#CHECKERR: set: Variable name '' is not valid. See `help identifiers`.
#CHECKERR: {{.*}}set.fish (line {{\d+}}):
#CHECKERR: {{.*}}set.fish (line {{\d+}}):
#CHECKERR: set "" foo
#CHECKERR: ^
#CHECKERR: (Type 'help set' for related documentation)
set --show ""
#CHECKERR: set: Variable name '' is not valid. See `help identifiers`.
#CHECKERR: {{.*}}set.fish (line {{\d+}}):
#CHECKERR: {{.*}}set.fish (line {{\d+}}):
#CHECKERR: set --show ""
#CHECKERR: ^
#CHECKERR: (Type 'help set' for related documentation)
@ -802,7 +803,7 @@ function test-function-scope
#CHECK: $globalvar: set in global scope, unexported, with 1 elements
#CHECK: $globalvar[1]: |global|
end
test-function-scope
echo $funcvar $funcvar2
# CHECK:

View File

@ -1,10 +1,8 @@
#RUN: %fish -C 'set -g fish %fish' %s
# Explicitly overriding HOME/XDG_CONFIG_HOME is only required if not invoking via `make test`
# RUN: env HOME="$(mktemp -d)" XDG_CONFIG_HOME="$(mktemp -d)" %fish -C 'set -g fish %fish' %s
set -gx XDG_CONFIG_HOME (mktemp -d)
set -gx XDG_DATA_HOME $XDG_CONFIG_HOME
mkdir -p $XDG_CONFIG_HOME/fish
# fish_variables
set -l target_file $XDG_CONFIG_HOME/fish/target_fish_variables
set -l fish_variables $XDG_CONFIG_HOME/fish/fish_variables

View File

@ -1,16 +1,16 @@
#RUN: %fish -C 'set -g fish %fish' %s
#REQUIRES: command -v tmux
# Resolve absolute path to fish (if needed) before changing directories
set fish (builtin realpath $fish)
# Isolated tmux.
set -g tmpdir (mktemp -d)
# Don't CD elsewhere, because tmux socket file is relative to CWD. Using
# absolute path to socket file is prone to 'socket file name too long' error.
# Isolated tmux. tmux can't handle session sockets in paths that are too long, and macOS has a very
# long $TMPDIR, so use a relative path - except macOS doesn't have `realpath --relative-to`...
# We are cd'd into a unique temp dir created/assigned by the test driver, so this will work so long
# as `tmux` is only invoked from the same PWD.
set tmpdir (command mktemp -d ./tmp.XXXXXXXX)
cd $tmpdir
set -g tmux tmux -S .tmux-socket -f /dev/null
set -g tmux tmux -S ./.tmux-socket -f /dev/null
set -g sleep sleep .1
set -q CI && set sleep sleep 1
@ -55,4 +55,3 @@ $tmux capture-pane -p
# CHECK: aabc aaBd
$tmux kill-server
rm -r $tmpdir

View File

@ -16,16 +16,15 @@ cd $oldpwd
rm -Rf $dir
# Verify that we can do wildcard expansion when we
# don't have read access to some path components
# Verify that we can do wildcard expansion when we don't have read access to some path components.
# See #2099
set -l where ../test/temp/fish_wildcard_permissions_test/noaccess/yesaccess
set -l where ./fish_wildcard_permissions_test/noaccess/yesaccess
mkdir -p $where
chmod 300 (dirname $where) # no read permissions
mkdir -p $where
# "__env.fish" here to confirm ordering - #6593.
touch $where/alpha.txt $where/beta.txt $where/delta.txt $where/__env.fish
echo $where/*
#CHECK: ../test/temp/fish_wildcard_permissions_test/noaccess/yesaccess/__env.fish ../test/temp/fish_wildcard_permissions_test/noaccess/yesaccess/alpha.txt ../test/temp/fish_wildcard_permissions_test/noaccess/yesaccess/beta.txt ../test/temp/fish_wildcard_permissions_test/noaccess/yesaccess/delta.txt
#CHECK: ./fish_wildcard_permissions_test/noaccess/yesaccess/__env.fish ./fish_wildcard_permissions_test/noaccess/yesaccess/alpha.txt ./fish_wildcard_permissions_test/noaccess/yesaccess/beta.txt ./fish_wildcard_permissions_test/noaccess/yesaccess/delta.txt
chmod 700 (dirname $where) # so we can delete it
rm -rf ../test/temp/fish_wildcard_permissions_test
rm -rf ./fish_wildcard_permissions_test

View File

@ -1,8 +1,6 @@
# Interactive tests using `expect`
#! /bin/echo "interactive.fish must be run via the test driver!"
#
# There is no shebang line because you shouldn't be running this by hand. You
# should be running it via `make test` to ensure the environment is properly
# setup.
# Interactive tests using `pexpect`
# Set this var to modify behavior of the code being tests. Such as avoiding running
# `fish_update_completions` when running tests.
@ -11,29 +9,22 @@ set -gx FISH_UNIT_TESTS_RUNNING 1
# Change to directory containing this script
cd (status dirname)
# These env vars should not be inherited from the user environment because they can affect the
# behavior of the tests. So either remove them or set them to a known value.
# See also tests/test.fish.
set -gx TERM xterm
set -e ITERM_PROFILE
# Test files specified on commandline, or all pexpect files.
if set -q argv[1]
set pexpect_files_to_test pexpects/$argv.py
if set -q argv[1] && test -n "$argv[1]"
set pexpect_files_to_test pexpects/$argv
else if set -q FISH_PEXPECT_FILES
set pexpect_files_to_test (string replace -r '^.*/(?=pexpects/)' '' -- $FISH_PEXPECT_FILES)
else
say -o cyan "Testing interactive functionality"
set pexpect_files_to_test pexpects/*.py
end
source test_util.fish (status -f) $argv
or exit
source test_util.fish || exit
cat interactive.config >>$XDG_CONFIG_HOME/fish/config.fish
say -o cyan "Testing interactive functionality"
function test_pexpect_file
set -l file $argv[1]
echo -n "Testing file $file ... "
echo -n "Testing file $file:"
begin
set starttime (timestamp)
@ -77,7 +68,11 @@ end
set failed (count $failed)
if test $failed -eq 0
say green "All interactive tests completed successfully"
if test (count $pexpect_files_to_test) -gt 1
say green "All interactive tests completed successfully"
else
say green "$pexpect_files_to_test completed successfully"
end
exit 0
else
set plural (test $failed -eq 1; or echo s)

View File

@ -19,31 +19,40 @@ else
set files_to_test checks/*.fish
end
# test_util handles the environment setup and then restarts us
source test_util.fish (status -f) $argv
or exit
say -o cyan "Testing high level script functionality"
# Be less verbose when running tests one-by-one
if test (count $files_to_test) -gt 1
say -o cyan "Testing high level script functionality"
end
set -g python (__fish_anypython)
# Test littlecheck files.
set littlecheck_files (string match '*.fish' -- $files_to_test)
if set -q littlecheck_files[1]
set -l skipped 0
set -l failed 0
if set -q files_to_test[1]
$python -S ../littlecheck.py \
--progress \
-s fish=../test/root/bin/fish \
-s fish_test_helper=../test/root/bin/fish_test_helper \
$littlecheck_files
set -l littlecheck_failures $status
set failed (math $failed + $littlecheck_failures)
$files_to_test
set -l littlecheck_status $status
if test "$littlecheck_status" -eq 125
# 125 indicates that all tests executed were skipped.
set skipped (count $files_to_test)
else
# The return code indicates the number of tests that failed
set failed $littlecheck_status
end
end
if test $failed -eq 0
say green "All high level script tests completed successfully"
if test $failed -eq 0 && test $skipped -gt 0
test (count $files_to_test) -gt 1 && say blue (count $files_to_test)" tests skipped"
exit 125
else if test $failed -eq 0
test (count $files_to_test) -gt 1 && say green "All high level script tests completed successfully"
exit 0
else
set plural (test $failed -eq 1; or echo s)
say red "$failed test$plural failed"
test (count $files_to_test) -gt 1 && say red "$failed tests failed"
exit 1
end

124
tests/test_driver.sh Normal file
View File

@ -0,0 +1,124 @@
# vim: set ts=4 sw=4 tw=100 et:
# POSIX sh test driver to reduce dependency on fish in tests
# macOS has really weird default IFS behavior that splits output in random places, and the trailing
# backspace is to prevent \n from being gobbled up by the subshell output substitution.
# Folks, this is why you should use fish!
IFS="$(printf "\n\b")"
# The first argument is the path to the script to launch; all remaining arguments are forwarded to
# the script.
fish_script="$1"
shift 1
script_args="${@}"
die() {
if test "$#" -ge 0; then
printf "%s\n" "$@" 1>&2
fi
exit 1
}
# To keep things sane and to make error messages comprehensible, do not use relative paths anywhere
# in this script. Instead, make all paths relative to one of these or $homedir."
TESTS_ROOT="$(dirname "$0")"
BUILD_ROOT="${TESTS_ROOT}/.."
# macOS (still!) doesn't have `readlink -f` or `realpath`. That's OK, this is just for aesthetics.
if command -v realpath 1>/dev/null 2>/dev/null; then
TESTS_ROOT="$(realpath --no-symlinks "${TESTS_ROOT}")"
BUILD_ROOT="$(realpath --no-symlinks "${BUILD_ROOT}")"
fi
if ! test -z "$__fish_is_running_tests"; then
echo "Recursive test invocation detected!" 1>&2
exit 10
fi
# Set up a test environment and re-run the original script. We do not share environments
# whatsoever between tests, so each test driver run sets up a new profile altogether.
# macOS 10.10 requires an explicit template for `mktemp` and will create the folder in the
# current directory unless told otherwise. Linux isn't guaranteed to have $TMPDIR set.
homedir="$(mktemp -d 2>/dev/null || mktemp -d "${TMPDIR}tmp.XXXXXXXXXX")"
XDG_DATA_HOME="$homedir/xdg_data_home"
export XDG_DATA_HOME
mkdir -p $XDG_DATA_HOME/fish || die
XDG_CONFIG_HOME="$homedir/xdg_config_home"
export XDG_CONFIG_HOME
mkdir -p $XDG_CONFIG_HOME/fish || die
XDG_RUNTIME_DIR="$homedir/xdg_runtime_dir"
export XDG_CONFIG_HOME
mkdir -p $XDG_RUNTIME_DIR/fish || die
# Create a temp/scratch directory for tests to use, if they want (tests shouldn't write to a
# shared temp folder).
TMPDIR="$homedir/temp"
mkdir ${TMPDIR}
export TMPDIR
# These are used read-only so it's OK to symlink instead of copy
rm -f "$XDG_CONFIG_HOME/fish/functions"
ln -s "$PWD/test_functions" "$XDG_CONFIG_HOME/fish/functions" || die "Failed to symlink"
# Set the function path at startup, referencing the default fish functions and the test-specific
# functions.
fish_init_cmd="set fish_function_path ${XDG_CONFIG_HOME}/fish/functions ${BUILD_ROOT}/share/functions"
__fish_is_running_tests="$homedir"
export __fish_is_running_tests
# Set locale information for consistent tests. Fish should work with a lot of locales but the
# tests assume an english UTF-8 locale unless they explicitly override this default. We do not
# want the users locale to affect the tests since they might, for example, change the wording of
# logged messages.
#
# TODO: set LANG to en_US.UTF-8 so we test the locale message conversions (i.e., gettext).
unset LANGUAGE
# Remove "LC_" env vars from the test environment
for key in $(env | grep -E "^LC_"| grep -oE "^[^=]+"); do
unset "$key"
done
# Set the desired lang/locale tests are hard-coded against
export LANG="C"
export LC_CTYPE="en_US.UTF-8"
# These env vars should not be inherited from the user environment because they can affect the
# behavior of the tests. So either remove them or set them to a known value.
# See also tests/interactive.fish.
export TERM=xterm
unset COLORTERM
unset INSIDE_EMACS
unset ITERM_PROFILE
unset KONSOLE_PROFILE_NAME
unset KONSOLE_VERSION
unset PANTHEON_TERMINAL_ID
unset TERM_PROGRAM
unset TERM_PROGRAM_VERSION
unset VTE_VERSION
unset WT_PROFILE_ID
unset XTERM_VERSION
# Set a marker to indicate whether colored output should be suppressed (used in `test_util.fish`)
suppress_color=""
if ! tty 0>&1 > /dev/null; then
suppress_color="yes"
fi
export suppress_color
# Source test util functions at startup
fish_init_cmd="${fish_init_cmd} && source ${TESTS_ROOT}/test_util.fish";
# Run the test script, but don't exec so we can do cleanup after it succeeds/fails. Each test is
# launched directly within its TMPDIR, so that the fish tests themselves do not need to refer to
# TMPDIR (to ensure their output as displayed in case of failure by littlecheck is reproducible).
(cd $TMPDIR; env HOME="$homedir" "${BUILD_ROOT}/test/root/bin/fish" \
--init-command "${fish_init_cmd}" \
"$fish_script" "$script_args")
test_status="$?"
rm -rf "$homedir"
exit "$test_status"

View File

@ -52,19 +52,16 @@ function mktemp
# So let's outlaw them anywhere besides the end.
# Similarly GNU mktemp requires at least 3 X's, BSD mktemp requires none. Let's require 3.
begin
set -l chars (string split '' -- $template)
set -l found_x
for c in $chars
if test $c = X
set found_x $found_x X
else if set -q found_x[1]
echo 'mktemp: X\'s may only occur at the end of the template' >&2
_mktemp_help >&2
exit 1
end
# Look for at least three Xs that are not the end of the template
if string match -rq -- 'XXX[^X].*$' "$template"
echo "mktemp: X's may only occur at the end of the template '$template'" >&2
_mktemp_help >&2
exit 1
end
if test (count $found_x) -lt 3
echo "mktemp: too few X's in template '$template'" >&2
# Look for too few X incidences at the end of the template
if ! string match -rq -- 'XXX$' "$template"
echo "mktemp: too few trailing X's in template '$template'" >&2
_mktemp_usage >&2
exit 1
end

View File

@ -1,94 +1,12 @@
# vim: set ts=4 sw=4 tw=100 et:
# Utilities for the test runners
if test "$argv[1]" = (status -f)
echo 'test_util.fish requires sourcing script as argument to `source`' >&2
echo 'use `source test_util.fish (status -f); or exit`' >&2
status --print-stack-trace >&2
exit 1
end
# Any remaining arguments are passed back to test.fish
set -l args_for_test_script
if set -q argv[2]
set args_for_test_script $argv[2..-1]
end
function die
set -q argv[1]; and echo $argv[1] >&2
exit 1
end
# Check if we're running in the test environment. If not, set it up and rerun fish with exec. The
# test is whether the special var __fish_is_running_tests exists and contains the same value as
# XDG_CONFIG_HOME. It checks the value and not just the presence because we're going to delete the
# config directory later if we're exiting successfully.
if not set -q __fish_is_running_tests
# Set up our test environment and re-run the original script.
set -l script $argv[1]
cd (builtin realpath (dirname $script))
or die
set -lx XDG_DATA_HOME ../test/xdg_data_home
rm -rf $XDG_DATA_HOME/fish
mkdir -p $XDG_DATA_HOME/fish; or die
set -lx XDG_CONFIG_HOME ../test/xdg_config_home
rm -rf $XDG_CONFIG_HOME/fish
mkdir -p $XDG_CONFIG_HOME/fish; or die
ln -s $PWD/test_functions $XDG_CONFIG_HOME/fish/functions; or die
set -l escaped_parent (builtin realpath $PWD/.. | string escape); or die
set -l escaped_config (string escape -- $XDG_CONFIG_HOME/fish)
printf 'set fish_function_path \'%s/functions\' \'%s/share/functions\'\n' $escaped_config $escaped_parent >$XDG_CONFIG_HOME/fish/config.fish; or die
set -xl __fish_is_running_tests $XDG_CONFIG_HOME
# Set locale information for consistent tests. Fish should work with a lot of locales but the
# tests assume an english UTF-8 locale unless they explicitly override this default. We do not
# want the users locale to affect the tests since they might, for example, change the wording of
# logged messages.
#
# TODO: set LANG to en_US.UTF-8 so we test the locale message conversions (i.e., gettext).
set -e LANGUAGE
set -x LANG C
# Remove "LC_" env vars from the test environment.
for var in (set -xn)
string match -q 'LC_*' $var
and set -e $var
end
set -x LC_CTYPE en_US.UTF-8
# These env vars should not be inherited from the user environment because they can affect the
# behavior of the tests. So either remove them or set them to a known value.
# See also tests/interactive.fish.
set -gx TERM xterm
set -e COLORTERM
set -e INSIDE_EMACS
set -e ITERM_PROFILE
set -e KONSOLE_PROFILE_NAME
set -e KONSOLE_VERSION
set -e PANTHEON_TERMINAL_ID
set -e TERM_PROGRAM
set -e TERM_PROGRAM_VERSION
set -e VTE_VERSION
set -e WT_PROFILE_ID
set -e XTERM_VERSION
exec ../test/root/bin/fish $script $args_for_test_script
die 'exec failed'
else if test "$__fish_is_running_tests" != "$XDG_CONFIG_HOME"
echo 'Something went wrong with the test runner.' >&2
echo "__fish_is_running_tests: $__fish_is_running_tests" >&2
echo "XDG_CONFIG_HOME: $XDG_CONFIG_HOME" >&2
exit 10
end
set -l suppress_color
if not tty 0>&1 >/dev/null
set suppress_color yes
end
# $suppress_color is set by `test_driver.sh` (via import of exported variables)
function say -V suppress_color
set -l color_flags
set -l suppress_newline