diff --git a/cmake/Tests.cmake b/cmake/Tests.cmake index 80b3ae79a..1d543d944 100644 --- a/cmake/Tests.cmake +++ b/cmake/Tests.cmake @@ -17,24 +17,17 @@ fish_link_deps_and_sign(fish_tests) # The "test" directory. set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) -# HACK: CMake is a configuration tool, not a build system. However, until CMake adds a way to -# dynamically discover tests, our options are either this or resorting to sed/awk to parse the low -# level tests source file to get the list of individual tests. Or to split each test into its own -# source file. -execute_process( - COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_SOURCE_DIR}/src/fish_tests.cpp - -o ${CMAKE_BINARY_DIR}/fish_tests_list - -I ${CMAKE_CURRENT_BINARY_DIR} # for config.h - -lpthread - # Strip actual dependency on fish code - -Wl,-undefined,dynamic_lookup,--unresolved-symbols=ignore-all - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) -execute_process( - COMMAND ./fish_tests_list --list - OUTPUT_FILE low_level_tests.txt - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) +# 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) @@ -76,37 +69,6 @@ add_custom_target(tests_buildroot_target ${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) - foreach(LTEST ${LOW_LEVEL_TESTS}) add_test( NAME ${LTEST} diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index dd13fbd1c..955ccd9b9 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -6647,6 +6647,129 @@ 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 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 + 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 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);