aboutsummaryrefslogtreecommitdiff
path: root/tests/functional/repl_characterization/test-session.hh
blob: c77cce6d53b1a4f2741ad76d9b8bc4e5ec4db812 (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
#pragma once
///@file

#include <functional>
#include <sched.h>
#include <span>
#include <string>

#include "file-descriptor.hh"
#include "tests/terminal-code-eater.hh"

namespace nix {

struct RunningProcess
{
    pid_t pid;
    Pipe procStdin;
    Pipe procStdout;

    static RunningProcess start(std::string executable, Strings args);
};

/** DFA that catches repl prompts */
class ReplOutputParser
{
public:
    ReplOutputParser(std::string prompt) : prompt(prompt)
    {
        assert(!prompt.empty());
    }
    /** Feeds in a character and returns whether this is an open prompt */
    bool feed(char c);

    enum class State {
        Prompt,
        Context,
    };

private:
    State state = State::Prompt;
    size_t pos_in_prompt = 0;
    std::string const prompt;

    void transition(State state, char responsible_char, bool wasPrompt = false);
};

struct TestSession
{
    RunningProcess proc;
    ReplOutputParser outputParser;
    TerminalCodeEater eater;
    std::string outLog;
    std::string prompt;

    TestSession(std::string prompt, RunningProcess && proc)
        : proc(std::move(proc))
        , outputParser(prompt)
        , eater{}
        , outLog{}
        , prompt(prompt)
    {
    }

    /** Waits for the prompt and then returns if a prompt was found */
    bool waitForPrompt();

    /** Feeds a line of input into the command */
    void runCommand(std::string command);

    /** Closes the session, closing standard input and waiting for standard
     * output to close, capturing any remaining output. */
    void close();

private:
    /** Waits until the command closes its output */
    void wait();

    enum class ReadOutThenCallbackResult { Stop, Continue };
    using ReadOutThenCallback = std::function<ReadOutThenCallbackResult(std::span<const char>)>;
    /** Reads some chunks of output, calling the callback provided for each
     * chunk and stopping if it returns Stop.
     *
     * @returns false if EOF, true if the callback requested we stop first.
     * */
    bool readOutThen(ReadOutThenCallback cb);
};
};