diff options
author | Rebecca Turner <rbt@sent.as> | 2024-03-22 16:45:05 -0700 |
---|---|---|
committer | Rebecca Turner <rbt@sent.as> | 2024-04-05 13:14:21 -0700 |
commit | ee423f391d33246801de86c73330c8442df09dc8 (patch) | |
tree | b2be1785079e942098925f414d33848069779281 /tests/functional/repl_characterization/repl_characterization.cc | |
parent | 83d720b7304f6479f48dca8f3062e966f359e9b4 (diff) |
Rewrite REPL test parser
- Use a recursive descent parser so that it's easy to extend.
- Add `@args` to enable customizing command-line arguments
- Add `@should-start` to enable `nix repl` tests that error before
entering the REPL
- Make sure to read all stdout output before comparing. This catches
some extra output we were tossing out before!
Change-Id: I5522555df4c313024ab15cd10f9f04e7293bda3a
Diffstat (limited to 'tests/functional/repl_characterization/repl_characterization.cc')
-rw-r--r-- | tests/functional/repl_characterization/repl_characterization.cc | 164 |
1 files changed, 111 insertions, 53 deletions
diff --git a/tests/functional/repl_characterization/repl_characterization.cc b/tests/functional/repl_characterization/repl_characterization.cc index 68a66b2f3..fa5a7ba74 100644 --- a/tests/functional/repl_characterization/repl_characterization.cc +++ b/tests/functional/repl_characterization/repl_characterization.cc @@ -1,16 +1,17 @@ #include <gtest/gtest.h> +#include <boost/algorithm/string/replace.hpp> +#include <optional> #include <string> #include <string_view> -#include <optional> #include <unistd.h> -#include <boost/algorithm/string/replace.hpp> +#include "escape-string.hh" #include "test-session.hh" -#include "util.hh" #include "tests/characterization.hh" #include "tests/cli-literate-parser.hh" #include "tests/terminal-code-eater.hh" +#include "util.hh" using namespace std::string_literals; @@ -40,92 +41,149 @@ public: return unitTestData + "/" + testStem; } - void runReplTest(std::string_view const & content, std::vector<std::string> extraArgs = {}) const + void runReplTest(const std::string content, std::vector<std::string> extraArgs = {}) const { - auto syntax = CLILiterateParser::parse(std::string(REPL_PROMPT), content); + auto parsed = cli_literate_parser::parse( + content, cli_literate_parser::Config{.prompt = std::string(REPL_PROMPT), .indent = 2} + ); + parsed.interpolatePwd(unitTestData); // FIXME: why does this need two --quiets - // show-trace is on by default due to test configuration, but is not a standard - Strings args{"--quiet", "repl", "--quiet", "--option", "show-trace", "false", "--offline", "--extra-experimental-features", "repl-automation"}; + // show-trace is on by default due to test configuration, but is not a + // standard + Strings args{ + "--quiet", + "repl", + "--quiet", + "--option", + "show-trace", + "false", + "--offline", + "--extra-experimental-features", + "repl-automation", + }; args.insert(args.end(), extraArgs.begin(), extraArgs.end()); + args.insert(args.end(), parsed.args.begin(), parsed.args.end()); auto nixBin = canonPath(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR)); auto process = RunningProcess::start(nixBin + "/nix", args); - auto session = TestSession{std::string(AUTOMATION_PROMPT), std::move(process)}; - - for (auto & bit : syntax) { - if (bit.kind != CLILiterateParser::NodeKind::COMMAND) { - continue; - } - - if (!session.waitForPrompt()) { - ASSERT_TRUE(false); - } - session.runCommand(bit.text); + auto session = TestSession(std::string(AUTOMATION_PROMPT), std::move(process)); + + for (auto & event : parsed.syntax) { + std::visit( + overloaded{ + [&](const cli_literate_parser::Command & e) { + ASSERT_TRUE(session.waitForPrompt()); + if (e.text == ":quit") { + // If we quit the repl explicitly, we won't have a + // prompt when we're done. + parsed.shouldStart = false; + } + session.runCommand(e.text); + }, + [&](const auto & e) {}, + }, + event + ); } - if (!session.waitForPrompt()) { - ASSERT_TRUE(false); + if (parsed.shouldStart) { + ASSERT_TRUE(session.waitForPrompt()); } session.close(); - auto replacedOutLog = boost::algorithm::replace_all_copy(session.outLog, unitTestData, "TEST_DATA"); + auto replacedOutLog = + boost::algorithm::replace_all_copy(session.outLog, unitTestData, "$TEST_DATA"); auto cleanedOutLog = trimOutLog(replacedOutLog); - auto parsedOutLog = CLILiterateParser::parse(std::string(AUTOMATION_PROMPT), cleanedOutLog, 0); + auto parsedOutLog = cli_literate_parser::parse( + std::string(cleanedOutLog), + cli_literate_parser::Config{.prompt = std::string(AUTOMATION_PROMPT), .indent = 0} + ); - parsedOutLog = CLILiterateParser::tidyOutputForComparison(std::move(parsedOutLog)); - syntax = CLILiterateParser::tidyOutputForComparison(std::move(syntax)); + auto expected = parsed.tidyOutputForComparison(); + auto actual = parsedOutLog.tidyOutputForComparison(); - ASSERT_EQ(parsedOutLog, syntax); + ASSERT_EQ(expected, actual); + } + + void runReplTestPath(const std::string_view & nameBase, std::vector<std::string> extraArgs) + { + auto nixPath = goldenMaster(nameBase + ".nix"); + if (pathExists(nixPath)) { + extraArgs.push_back("-f"); + extraArgs.push_back(nixPath); + } + readTest(nameBase + ".test", [this, extraArgs](std::string input) { + runReplTest(input, extraArgs); + }); + } + + void runReplTestPath(const std::string_view & nameBase) + { + runReplTestPath(nameBase, {}); + } + + void runDebuggerTest(const std::string_view & nameBase) + { + runReplTestPath(nameBase, {"--debugger"}); } }; -TEST_F(ReplSessionTest, parses) +TEST_F(ReplSessionTest, round_trip) { - writeTest("basic.ast", [this]() { + writeTest("basic.test", [this]() { const std::string content = readFile(goldenMaster("basic.test")); - auto parser = CLILiterateParser{std::string(REPL_PROMPT)}; - parser.feed(content); + auto parsed = cli_literate_parser::parse( + content, cli_literate_parser::Config{.prompt = std::string(REPL_PROMPT)} + ); std::ostringstream out{}; - for (auto & bit : parser.syntax()) { - out << bit.print() << "\n"; + for (auto & node : parsed.syntax) { + cli_literate_parser::unparseNode(out, node, true); } return out.str(); }); +} +TEST_F(ReplSessionTest, tidy) +{ + writeTest("basic.ast", [this]() { + const std::string content = readFile(goldenMaster("basic.test")); + auto parsed = cli_literate_parser::parse( + content, cli_literate_parser::Config{.prompt = std::string(REPL_PROMPT)} + ); + std::ostringstream out{}; + for (auto & node : parsed.syntax) { + out << debugNode(node) << "\n"; + } + return out.str(); + }); writeTest("basic_tidied.ast", [this]() { const std::string content = readFile(goldenMaster("basic.test")); - auto syntax = CLILiterateParser::parse(std::string(REPL_PROMPT), content); - - syntax = CLILiterateParser::tidyOutputForComparison(std::move(syntax)); - + auto parsed = cli_literate_parser::parse( + content, cli_literate_parser::Config{.prompt = std::string(REPL_PROMPT)} + ); + auto tidied = parsed.tidyOutputForComparison(); std::ostringstream out{}; - for (auto & bit : syntax) { - out << bit.print() << "\n"; + for (auto & node : tidied) { + out << debugNode(node) << "\n"; } return out.str(); }); } -TEST_F(ReplSessionTest, repl_basic) -{ - readTest("basic_repl.test", [this](std::string input) { runReplTest(input); }); -} - -#define DEBUGGER_TEST(name) \ +#define REPL_TEST(name) \ TEST_F(ReplSessionTest, name) \ - { \ - readTest(#name ".test", [this](std::string input) { \ - runReplTest(input, {"--debugger", "-f", goldenMaster(#name ".nix")}); \ - }); \ + { \ + runReplTestPath(#name); \ } -DEBUGGER_TEST(regression_9918); -DEBUGGER_TEST(regression_9917); -DEBUGGER_TEST(regression_l145); -DEBUGGER_TEST(stack_vars); -DEBUGGER_TEST(no_nested_debuggers); +REPL_TEST(basic_repl); +REPL_TEST(no_nested_debuggers); +REPL_TEST(regression_9917); +REPL_TEST(regression_9918); +REPL_TEST(regression_l145); +REPL_TEST(stack_vars); -}; +}; // namespace nix |