Rebecca Turner
ee423f391d
- 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
88 lines
2 KiB
C++
88 lines
2 KiB
C++
#pragma once
|
|
///@file
|
|
|
|
#include <functional>
|
|
#include <sched.h>
|
|
#include <span>
|
|
#include <string>
|
|
|
|
#include "util.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<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);
|
|
};
|
|
};
|