[tests] add an initial TUI test

This commit is contained in:
Timothy Stack 2021-02-02 21:58:42 -08:00
parent 37523fe7d0
commit 0a701394fe
10 changed files with 304 additions and 47 deletions

View File

@ -1,6 +1,6 @@
# aminclude_static.am generated automatically by Autoconf
# from AX_AM_MACROS_STATIC on Mon Feb 1 09:06:23 PST 2021
# from AX_AM_MACROS_STATIC on Tue Feb 2 19:06:51 PST 2021
# Code coverage

View File

@ -1398,7 +1398,7 @@ static void looper()
bool initial_rescan_completed = false;
int session_stage = 0;
rlc.do_update();
// rlc.do_update();
while (lnav_data.ld_looping) {
vector<struct pollfd> pollfds;
@ -1501,7 +1501,24 @@ static void looper()
0
});
if (initial_build) {
view_curses::awaiting_user_input();
switch (lnav_data.ld_mode) {
case LNM_COMMAND:
case LNM_SEARCH:
case LNM_SEARCH_FILTERS:
case LNM_SEARCH_FILES:
case LNM_SQL:
case LNM_EXEC:
case LNM_USER:
if (rlc.consume_ready_for_input()) {
log_debug("waiting for readline input")
view_curses::awaiting_user_input();
}
break;
default:
log_debug("waiting for paging input");
view_curses::awaiting_user_input();
break;
}
}
}
rlc.update_poll_set(pollfds);

View File

@ -52,6 +52,7 @@
#endif
#include <string>
#include <utility>
#include "base/string_util.hh"
#include "fmt/format.h"
@ -89,8 +90,6 @@ static const char *RL_INIT[] = {
"set menu-complete-display-prefix on",
"TAB: menu-complete",
"\"\\e[Z\": menu-complete-backward",
NULL
};
readline_context *readline_context::loaded_context;
@ -390,7 +389,7 @@ char **readline_context::attempted_completion(const char *text,
}
retval = rl_completion_matches(text, completion_generator);
if (retval == NULL) {
if (retval == nullptr) {
rl_attempted_completion_over = 1;
}
@ -427,10 +426,10 @@ int readline_context::command_complete(int count, int key)
return rl_insert(count, key);
}
readline_context::readline_context(const std::string &name,
readline_context::readline_context(std::string name,
readline_context::command_map_t *commands,
bool case_sensitive)
: rc_name(name),
: rc_name(std::move(name)),
rc_case_sensitive(case_sensitive),
rc_quote_chars("\"'"),
rc_highlighter(nullptr)
@ -581,8 +580,8 @@ void readline_curses::start()
if (openpty(this->rc_pty[RCF_MASTER].out(),
this->rc_pty[RCF_SLAVE].out(),
NULL,
NULL,
nullptr,
nullptr,
&ws) < 0) {
perror("error: failed to open terminal(openpty)");
throw error(errno);
@ -622,8 +621,8 @@ void readline_curses::start()
rl_add_defun("alt-done", alt_done_func, '\x0a');
// rl_add_defun("command-complete", readline_context::command_complete, ' ');
for (int lpc = 0; RL_INIT[lpc]; lpc++) {
snprintf(buffer, sizeof(buffer), "%s", RL_INIT[lpc]);
for (const auto* init_cmd : RL_INIT) {
snprintf(buffer, sizeof(buffer), "%s", init_cmd);
rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
}
@ -668,7 +667,7 @@ void readline_curses::start()
itv.it_value.tv_usec = KEY_TIMEOUT;
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, NULL);
setitimer(ITIMER_REAL, &itv, nullptr);
rl_callback_read_char();
if (RL_ISSTATE(RL_STATE_DONE) && !got_line) {
@ -704,6 +703,13 @@ void readline_curses::start()
}
last_h1 = h1;
last_h2 = h2;
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
'w',
"",
0) != 0) {
perror("line: write failed");
_exit(1);
}
}
}
if (FD_ISSET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds)) {
@ -749,6 +755,13 @@ void readline_curses::start()
perror("line: write failed");
_exit(1);
}
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
'w',
"",
0) != 0) {
perror("line: write failed");
_exit(1);
}
}
else if (strcmp(msg, "a") == 0) {
char reply[4];
@ -772,7 +785,7 @@ void readline_curses::start()
&context,
type,
&prompt_start) == 2) {
require(this->rc_contexts[context] != NULL);
require(this->rc_contexts[context] != nullptr);
this->rc_contexts[context]->rc_prefixes[string(type)] =
string(&msg[prompt_start]);
@ -782,7 +795,7 @@ void readline_curses::start()
&context,
type,
&prompt_start) == 2) {
require(this->rc_contexts[context] != NULL);
require(this->rc_contexts[context] != nullptr);
this->rc_contexts[context]->
add_possibility(string(type),
@ -793,7 +806,7 @@ void readline_curses::start()
&context,
type,
&prompt_start) == 2) {
require(this->rc_contexts[context] != NULL);
require(this->rc_contexts[context] != nullptr);
this->rc_contexts[context]->
rem_possibility(string(type),
@ -829,33 +842,30 @@ void readline_curses::start()
itv.it_value.tv_usec = 0;
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &itv, NULL) < 0) {
if (setitimer(ITIMER_REAL, &itv, nullptr) < 0) {
log_error("setitimer: %s", strerror(errno));
}
current_context->second->save();
current_context = this->rc_contexts.end();
}
if (got_winch) {
struct winsize ws;
struct winsize new_ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_ws) == -1) {
throw error(errno);
}
got_winch = 0;
rl_set_screen_size(ws.ws_row, ws.ws_col);
rl_set_screen_size(new_ws.ws_row, new_ws.ws_col);
}
}
auto config_dir = dotlnav_path();
std::map<int, readline_context *>::iterator citer;
for (citer = this->rc_contexts.begin();
citer != this->rc_contexts.end();
++citer) {
citer->second->load();
for (auto& pair : this->rc_contexts) {
pair.second->load();
auto hpath = (config_dir / citer->second->get_name()).string() + ".history";
auto hpath = (config_dir / pair.second->get_name()).string() + ".history";
write_history(hpath.c_str());
citer->second->save();
pair.second->save();
}
_exit(0);
@ -1032,6 +1042,9 @@ void readline_curses::check_poll_set(const vector<struct pollfd> &pollfds)
this->rc_change(this);
this->rc_display_match(this);
break;
case 'w':
this->rc_ready_for_input = true;
break;
}
}
}

View File

@ -95,7 +95,7 @@ public:
} command_t;
typedef std::map<std::string, command_t *> command_map_t;
readline_context(const std::string &name,
readline_context(std::string name,
command_map_t *commands = nullptr,
bool case_sensitive = true);
@ -364,6 +364,13 @@ public:
return this->rc_max_match_length;
};
bool consume_ready_for_input() {
auto retval = this->rc_ready_for_input;
this->rc_ready_for_input = false;
return retval;
}
private:
enum {
RCF_MASTER,
@ -391,6 +398,7 @@ private:
int rc_match_index{0};
std::vector<std::string> rc_matches;
bool rc_is_alt_focus{false};
bool rc_ready_for_input{false};
action rc_change;
action rc_perform;

View File

@ -175,6 +175,7 @@ dist_noinst_SCRIPTS = \
test_sql_time_func.sh \
test_sql_xml_func.sh \
test_sql_fs_func.sh \
test_tui.sh \
test_view_colors.sh \
test_vt52_curses.sh \
test_pretty_print.sh
@ -306,7 +307,8 @@ dist_noinst_DATA = \
formats/timestamp/format.json \
log-samples/sample-27353a72ba4025448f261dcfa6ea16e474187795.txt \
log-samples/sample-70c906b3c1a1cf03f15bde92ee78edfa6f9b7960.txt \
log-samples/sample-ad31f12d2adabd07e3ddda3ad5b0dbf6b49c4c99.txt
log-samples/sample-ad31f12d2adabd07e3ddda3ad5b0dbf6b49c4c99.txt \
tui-captures/tui_help.0
TESTS = \
lnav_doctests \
@ -341,6 +343,7 @@ TESTS = \
test_sql_str_func.sh \
test_sql_time_func.sh \
test_sql_xml_func.sh \
test_tui.sh \
test_data_parser.sh \
test_pretty_print.sh \
test_view_colors.sh \

View File

@ -106,7 +106,6 @@ int main(int argc, char *argv[])
"\x02",
"\a",
"ab\bcdef",
0
};
screen_curses sc;
@ -115,14 +114,15 @@ int main(int argc, char *argv[])
vt.set_window(sc.get_window());
vt.set_width(10);
for (lpc = 0; CANNED_INPUT[lpc]; lpc++) {
vt.map_output(CANNED_INPUT[lpc], strlen(CANNED_INPUT[lpc]));
for (const auto* canned : CANNED_INPUT) {
vt.map_output(canned, strlen(canned));
vt.do_update();
refresh();
view_curses::awaiting_user_input();
getch();
}
view_curses::awaiting_user_input();
getch();
}

View File

@ -246,6 +246,19 @@ static std::vector<char> hex2bits(const char *src)
return retval;
}
static const char *tstamp()
{
static char buf[64];
struct timeval tv;
gettimeofday(&tv, nullptr);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.", localtime(&tv.tv_sec));
auto dlen = strlen(buf);
snprintf(&buf[dlen], sizeof(buf) - dlen, "%.06d", tv.tv_usec);
return buf;
}
typedef enum {
CT_WRITE,
} command_type_t;
@ -379,17 +392,17 @@ struct term_machine {
void flush_line() {
if (std::exchange(this->tm_waiting_on_input, false) &&
!this->tm_user_input.empty()) {
fprintf(stderr, "flush keys\n");
fprintf(stderr, "%s:flush keys\n", tstamp());
fprintf(scripty_data.sd_from_child, "K ");
dump_memory(scripty_data.sd_from_child,
this->tm_user_input.data(),
this->tm_user_input.size());
1);
fprintf(scripty_data.sd_from_child, "\n");
this->tm_user_input.clear();
this->tm_user_input.erase(this->tm_user_input.begin());
}
if (this->tm_new_data || !this->tm_line_attrs.empty()) {
// fprintf(scripty_data.sd_from_child, "flush %d\n", this->tm_flush_count);
fprintf(stderr, "flush %d\n", this->tm_flush_count++);
fprintf(stderr, "%s:flush %d\n", tstamp(), this->tm_flush_count++);
fprintf(scripty_data.sd_from_child,
"S % 3d \u250B",
this->tm_cursor_y);
@ -607,6 +620,17 @@ struct term_machine {
this->tm_cursor_x += count;
break;
}
case 'B': {
auto amount = this->get_m_params();
int count = 1;
if (!amount.empty()) {
count = amount[0];
}
this->flush_line();
this->tm_cursor_y += count;
break;
}
case 'J': {
auto param = this->get_m_params();
@ -706,7 +730,7 @@ struct term_machine {
break;
}
default:
fprintf(stderr, "missed %c\n", ch);
fprintf(stderr, "%s:missed %c\n", tstamp(), ch);
this->add_line_attr(this->tm_escape_buffer.data());
break;
}
@ -840,7 +864,7 @@ int main(int argc, char *argv[])
case 'e':
scripty_data.sd_expected_name = optarg;
if ((file = fopen(optarg, "r")) == nullptr) {
fprintf(stderr, "error: cannot open %s\n", optarg);
fprintf(stderr, "%s:error: cannot open %s\n", tstamp(), optarg);
retval = EXIT_FAILURE;
} else {
char line[32 * 1024];
@ -866,7 +890,7 @@ int main(int argc, char *argv[])
prompt = true;
break;
default:
fprintf(stderr, "error: unknown flag -- %c\n", c);
fprintf(stderr, "%s:error: unknown flag -- %c\n", tstamp(), c);
retval = EXIT_FAILURE;
break;
}
@ -902,7 +926,7 @@ int main(int argc, char *argv[])
fd = open("/tmp/scripty.err", O_WRONLY | O_CREAT | O_APPEND, 0666);
dup2(fd, STDERR_FILENO);
close(fd);
fprintf(stderr, "startup\n");
fprintf(stderr, "%s:startup\n", tstamp());
child_term ct(passin);
@ -929,7 +953,7 @@ int main(int argc, char *argv[])
FD_SET(STDIN_FILENO, &read_fds);
FD_SET(ct.get_fd(), &read_fds);
fprintf(stderr, "goin in the loop\n");
fprintf(stderr, "%s:goin in the loop\n", tstamp());
tty_raw(STDIN_FILENO);
@ -948,14 +972,14 @@ int main(int argc, char *argv[])
case EINTR:
break;
default:
fprintf(stderr, "select %s\n", strerror(errno));
fprintf(stderr, "%s:select %s\n", tstamp(), strerror(errno));
scripty_data.sd_looping = false;
break;
}
} else {
char buffer[1024];
fprintf(stderr, "fds ready %d\n", rc);
fprintf(stderr, "%s:fds ready %d\n", tstamp(), rc);
gettimeofday(&now, nullptr);
timersub(&now, &last, &diff);
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
@ -968,13 +992,14 @@ int main(int argc, char *argv[])
log_perror(write(ct.get_fd(), buffer, rc));
for (ssize_t lpc = 0; lpc < rc; lpc++) {
fprintf(stderr, "%s:to-child %02x\n", tstamp(), buffer[lpc] & 0xff);
tm.new_user_input(buffer[lpc]);
}
}
}
if (FD_ISSET(ct.get_fd(), &ready_rfds)) {
rc = read(ct.get_fd(), buffer, sizeof(buffer));
fprintf(stderr, "read rc %d\n", rc);
fprintf(stderr, "%s:read rc %d\n", tstamp(), rc);
if (rc <= 0) {
scripty_data.sd_looping = false;
} else {
@ -982,8 +1007,11 @@ int main(int argc, char *argv[])
log_perror(write(STDOUT_FILENO, buffer, rc));
if (scripty_data.sd_from_child != nullptr) {
for (size_t lpc = 0; lpc < rc; lpc++) {
fprintf(stderr, "ch %02x\n",
#if 0
fprintf(stderr, "%s:from-child %02x\n",
tstamp(),
buffer[lpc] & 0xff);
#endif
tm.new_input(buffer[lpc]);
}
}
@ -1026,7 +1054,7 @@ int main(int argc, char *argv[])
}
}
else {
fprintf(stderr, "error: mismatch\n");
fprintf(stderr, "%s:error: mismatch\n", tstamp());
retval = EXIT_FAILURE;
}
}

8
test/test_tui.sh Normal file
View File

@ -0,0 +1,8 @@
#! /bin/bash
lnav_test="${top_builddir}/src/lnav-test"
run_test ./scripty -n -e ${srcdir}/tui-captures/tui_help.0 -- \
${lnav_test} -H < /dev/null
on_error_fail_with "help screen does not work?"

View File

@ -0,0 +1,179 @@
CSI Don't Send Mouse X & Y
CSI Dont Use Cell Motion Mouse Tracking
CSI Don't ...
CTRL Use alt charset
CTRL save cursor
CSI Use alternate screen buffer
CSI set scrolling region 1-24
S -1 ┋ ┋
A └ normal
CSI Replace mode
CSI Application cursor keys
CTRL =
OSC Set window title: LOG
S -1 ┋ ┋
A └ normal, normal, normal
CSI Erase all
S 1 ┋ Thu Jun 06 1 :: :: LOG ┋
A └ fg(#c0c0c0), bg(#008080)
S 2 ┋ x┋
A ···············································································├ normal
A └┛ alt
A ················································································└ normal
S 3 ┋ x┋
A └┛ alt
A ················································································└ normal
S 4 ┋ x┋
A └┛ alt
A ················································································└ normal
S 5 ┋ x┋
A └┛ alt
A ················································································└ normal
S 6 ┋ x┋
A └┛ alt
A ················································································└ normal
S 7 ┋ x┋
A └┛ alt
A ················································································└ normal
S 8 ┋ x┋
A └┛ alt
A ················································································└ normal
S 9 ┋ x┋
A └┛ alt
A ················································································└ normal
S 10 ┋ x┋
A └┛ alt
A ················································································└ normal
S 11 ┋ x┋
A └┛ alt
A ················································································└ normal
S 12 ┋ x┋
A └┛ alt
A ················································································└ normal
S 13 ┋ x┋
A └┛ alt
A ················································································└ normal
S 14 ┋ x┋
A └┛ alt
A ················································································└ normal
S 15 ┋ x┋
A └┛ alt
A ················································································└ normal
S 16 ┋ x┋
A └┛ alt
A ················································································└ normal
S 17 ┋ lqqqq No log messages; Files: 0; Error rate: 0.00/min; Time span: None qqqqk x┋
A └----┛ alt │ │ │ │ │ │ │ ││ │││
A ·······························└ bold │ │ │ │ ││ │││
A ·································└ normal │ │ │ │ ││ │││
A ···································└ fg(#800000), bold │ ││ │││
A ·············································└ normal │ ││ │││
A ···············································└ bold │ ││ │││
A ···················································└ normal │ ││ │││
A ····································································└ bold │││
A ········································································└ normal│
A ·········································································├ bold││
A └----┛ alt
A ··············································································└ normal
A └┛ alt
A ················································································└ normal
S 18 ┋ Files :: Text Filters :: Press q to exit ┋
A └ fg(#c0c0c0), bg(#000080), bold ││
A ·└ fg(#008080), bg(#000080), underline ││
A ··└ normal, fg(#c0c0c0), bg(#000080), bold ││
A ·······└ normal, fg(#c0c0c0), bg(#000080) ││
A ········└ fg(#000080), bg(#c0c0c0) ││
A ·········└ fg(#000000), bg(#c0c0c0), bold ││
A ··········└ fg(#800080), bg(#c0c0c0), underline ││
A ···········└ normal, fg(#000000), bg(#c0c0c0), bold ││
A ·······················└ normal, fg(#000000), bg(#c0c0c0) ││
A ······································································└ bold
A ·······································································└ normal, fg(#000000), bg(#c0c0c0)
S 19 ┋ ┋
S 20 ┋ x┋
A ···············································································├ normal
A └┛ alt
A ················································································└ normal
S 21 ┋ x┋
A └┛ alt
A ················································································└ normal
S 22 ┋ x┋
A └┛ alt
A ················································································└ normal
S 23 ┋ L0 0% ?:View Help ┋
A └ fg(#000000), bg(#c0c0c0)
S 22 ┋ ┋
A └ normal, normal
OSC Set window title: HELP
S 1 ┋ Thu Jun 06 1 :: :: HELP ┋
A └ fg(#000000), bg(#c0c0c0) │││ │││
A ················································└ fg(#008080), bg(#c0c0c0)
A ·················································└ fg(#c0c0c0), bg(#008080)
A ··················································└ fg(#000000), bg(#008080)
A ······································································└ fg(#000080), bg(#008080)
A ·······································································└ fg(#008080), bg(#000080)
A ········································································└ fg(#c0c0c0), bg(#000080), bold
S 2 ┋ x┋
A ···············································································└ normal, fg(#000000), bg(#c0c0c0)
A ················································································└ normal
A └┛ alt
A ················································································└ normal
S 3 ┋ lnav - A fancy log file viewer ┋
A ··└ fg(#000000), bg(#c0c0c0), normal
A ································└ carriage-return
S 5 ┋DESCRIPTION ┋
A ···········└ carriage-return
S 6 ┋=========== ┋
A ···········└ carriage-return
S 8 ┋The log file navigator, lnav, is an enhanced log file viewer that ┋
A ·································································└ carriage-return
S 9 ┋takes advantage of any semantic information that can be gleaned from ┋
A ····································································└ carriage-return
S 10 ┋the files being viewed, such as timestamps and log levels. Using this ┋
A ······································································└ carriage-return
S 11 ┋extra semantic information, lnav can do things like interleaving ┋
A ································································└ carriage-return
S 12 ┋messages from different files, generate histograms of messages over ┋
A ···································································└ carriage-return
S 13 ┋time, and providing hotkeys for navigating through the file. It is ┋
A ···································································└ carriage-return
S 14 ┋hoped that these features will allow the user to quickly and ┋
A ····························································└ carriage-return
S 15 ┋efficiently zero in on problems. ┋
A ································└ carriage-return
S 17 ┋ ┋
A ··············································································└ carriage-return
S 18 ┋OPENING PATHS/URLs x┋
A └┛ alt
A ················································································└ normal
S 19 ┋================== x┋
A └┛ alt
A ················································································└ normal
S 21 ┋The main arguments to lnav are the files, directories, glob patterns, ┋
A ·····································································└ carriage-return
S 22 ┋or URLs to be viewed. If no arguments are given, the default syslog ┋
S 24 ┋ Press e/E to move forward/backward through error message ┋
A ·····························└ bold │
A ······························└ normal │
A ·······························└ bold │
A ································└ normal │
A ···············································································└ backspace, backspace
A ··············································································└ [4h
CSI Replace mode
S 24 ┋ ┋
A ···············································································└ carriage-return
A └ normal
K 71
OSC Set window title: LOG
S 1 ┋ LOG ┋
A ···········································································└ fg(#c0c0c0), bg(#000080), bold
A ···············································································└ carriage-return
S 24 ┋ ┋
A └ normal, normal
CSI Erase all
CSI Use normal screen buffer
CTRL restore cursor
S 24 ┋ ┋
A └ carriage-return
CSI Normal cursor keys
CTRL Normal keypad

View File

@ -38,7 +38,8 @@ K 0d
CTRL bell
K 0d
S 1 ┋acdef ┋
K 0d0d
K 0d
K 0d
CSI Erase all
CSI Use normal screen buffer
CTRL restore cursor