diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 3d822569d..0a2153a18 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1884,26 +1884,47 @@ static void test_lru() { do_test(cache.evicted.size() == size_t(total_nodes)); } -/// A crappy environment_t that only knows about PWD. -struct pwd_environment_t : public environment_t { - std::map extras; +/// An environment built around an std::map. +struct test_environment_t : public environment_t { + std::map vars; virtual maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override { UNUSED(mode); - if (key == L"PWD") { - return env_var_t{wgetcwd(), 0}; + auto iter = vars.find(key); + if (iter != vars.end()) { + return env_var_t(iter->second, ENV_DEFAULT); } - auto extra = extras.find(key); - if (extra != extras.end()) { - return env_var_t(extra->second, ENV_DEFAULT); - } - return {}; + return none(); } wcstring_list_t get_names(int flags) const override { UNUSED(flags); - return {L"PWD"}; + wcstring_list_t result; + for (const auto &kv : vars) { + result.push_back(kv.first); + } + return result; + } +}; + +/// A test environment that knows about PWD. +struct pwd_environment_t : public test_environment_t { + virtual maybe_t get(const wcstring &key, + env_mode_flags_t mode = ENV_DEFAULT) const override { + if (key == L"PWD") { + return env_var_t{wgetcwd(), 0}; + } + return test_environment_t::get(key, mode); + } + + wcstring_list_t get_names(int flags) const override { + auto res = test_environment_t::get_names(flags); + res.clear(); + if (std::count(res.begin(), res.end(), L"PWD") == 0) { + res.push_back(L"PWD"); + } + return res; } }; @@ -3323,7 +3344,7 @@ static void test_autosuggest_suggest_special() { const wcstring wd = L"test/autosuggest_test"; pwd_environment_t vars{}; - vars.extras[L"HOME"] = parser_t::principal_parser().vars().get(L"HOME")->as_string(); + vars.vars[L"HOME"] = parser_t::principal_parser().vars().get(L"HOME")->as_string(); perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", vars, __LINE__); perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", vars, __LINE__); @@ -3352,7 +3373,7 @@ static void test_autosuggest_suggest_special() { perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", vars, __LINE__); - vars.extras[L"AUTOSUGGEST_TEST_LOC"] = wd; + vars.vars[L"AUTOSUGGEST_TEST_LOC"] = wd; perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", vars, __LINE__); perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", vars, __LINE__); @@ -3934,6 +3955,7 @@ class history_tests_t { public: static void test_history(); static void test_history_merge(); + static void test_history_path_detection(); static void test_history_formats(); // static void test_history_speed(void); static void test_history_races(); @@ -4286,6 +4308,73 @@ void history_tests_t::test_history_merge() { everything->clear(); } +void history_tests_t::test_history_path_detection() { + // Regression test for #7582. + say(L"Testing history path detection"); + char tmpdirbuff[] = "/tmp/fish_test_history.XXXXXX"; + wcstring tmpdir = str2wcstring(mkdtemp(tmpdirbuff)); + if (! string_suffixes_string(L"/", tmpdir)) { + tmpdir.push_back(L'/'); + } + + // Place one valid file in the directory. + wcstring filename = L"testfile"; + std::string path = wcs2string(tmpdir + filename); + FILE *f = fopen(path.c_str(), "w"); + if (!f) { + err(L"Failed to open test file from history path detection"); + return; + } + fclose(f); + + test_environment_t vars; + vars.vars[L"PWD"] = tmpdir; + vars.vars[L"HOME"] = tmpdir; + + history_t &history = history_t::history_with_name(L"path_detection"); + history.add_pending_with_file_detection(L"cmd0 not/a/valid/path", tmpdir); + history.add_pending_with_file_detection(L"cmd1 " + filename, tmpdir); + history.add_pending_with_file_detection(L"cmd2 " + tmpdir + L"/" + filename, tmpdir); + history.resolve_pending(); + + constexpr size_t hist_size = 3; + if (history.size() != hist_size) { + err(L"history has wrong size: %lu but expected %lu", (unsigned long)history.size(), (unsigned long)hist_size); + history.clear(); + return; + } + + // Expected sets of paths. + wcstring_list_t expected[hist_size] = { + {}, + {filename}, + {tmpdir + L"/" + filename}, + }; + + size_t lap; + const size_t maxlap = 128; + for (lap = 0; lap < maxlap; lap++) { + int failures = 0; + bool last = (lap + 1 == maxlap); + for (size_t i = 1; i <= hist_size; i++) { + if (history.item_at_index(i).required_paths != expected[hist_size - i]) { + failures += 1; + if (last) { + err(L"Wrong detected paths for item %lu", (unsigned long)i); + } + } + } + if (failures == 0) { + break; + } + // The file detection takes a little time since it occurs in the background. + // Loop until the test passes. + usleep(1E6 / 500); // 1 msec + } + //fprintf(stderr, "History saving took %lu laps\n", (unsigned long)lap); + history.clear(); +} + static bool install_sample_history(const wchar_t *name) { wcstring path; if (!path_get_data(path)) { @@ -6198,6 +6287,7 @@ int main(int argc, char **argv) { 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();