aboutsummaryrefslogtreecommitdiff
path: root/tests/functional/repl_characterization/repl_characterization.cc
blob: 200eda1ee7ea943bda72e30e45c9353ba712abde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <gtest/gtest.h>

#include <string>
#include <string_view>
#include <optional>
#include <unistd.h>

#include "test-session.hh"
#include "util.hh"
#include "tests/characterization.hh"
#include "tests/cli-literate-parser.hh"
#include "tests/terminal-code-eater.hh"

using namespace std::string_literals;

namespace nix {

static constexpr const char * REPL_PROMPT = "nix-repl> ";

// ASCII ENQ character
static constexpr const char * AUTOMATION_PROMPT = "\x05";

static std::string_view trimOutLog(std::string_view outLog)
{
    const std::string trailer = "\n"s + AUTOMATION_PROMPT;
    if (outLog.ends_with(trailer)) {
        outLog.remove_suffix(trailer.length());
    }
    return outLog;
}

class ReplSessionTest : public CharacterizationTest
{
    Path unitTestData = getUnitTestData();

public:
    Path goldenMaster(std::string_view testStem) const override
    {
        return unitTestData + "/" + testStem;
    }

    void runReplTest(std::string_view const & content, std::vector<std::string> extraArgs = {}) const
    {
        auto syntax = CLILiterateParser::parse(REPL_PROMPT, content);

        // 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"};
        args.insert(args.end(), extraArgs.begin(), extraArgs.end());

        auto nixBin = canonPath(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR));

        auto process = RunningProcess::start(nixBin + "/nix", args);
        auto session = TestSession{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);
        }
        if (!session.waitForPrompt()) {
            ASSERT_TRUE(false);
        }
        session.close();

        auto parsedOutLog = CLILiterateParser::parse(AUTOMATION_PROMPT, trimOutLog(session.outLog), 0);

        parsedOutLog = CLILiterateParser::tidyOutputForComparison(std::move(parsedOutLog));
        syntax = CLILiterateParser::tidyOutputForComparison(std::move(syntax));

        ASSERT_EQ(parsedOutLog, syntax);
    }
};

TEST_F(ReplSessionTest, parses)
{
    writeTest("basic.ast", [this]() {
        const std::string content = readFile(goldenMaster("basic.test"));
        auto parser = CLILiterateParser{REPL_PROMPT};
        parser.feed(content);

        std::ostringstream out{};
        for (auto & bit : parser.syntax()) {
            out << bit.print() << "\n";
        }
        return out.str();
    });
}

TEST_F(ReplSessionTest, repl_basic)
{
    readTest("basic_repl.test", [this](std::string input) { runReplTest(input); });
}

#define DEBUGGER_TEST(name) \
    TEST_F(ReplSessionTest, name) \
    { \
        readTest(#name ".test", [this](std::string input) { \
            runReplTest(input, {"--debugger", "-f", goldenMaster(#name ".nix")}); \
        }); \
    }

DEBUGGER_TEST(regression_9918);
DEBUGGER_TEST(regression_9917);

};