#!/bin/sh # Run hledger-ui in the background without needing an interactive # terminal, passing through any arguments, and dump its first screen* # as cleaned up plain text. This allows scripts to run hledger-ui and # capture its output. # * Or, all output from inputs hard-coded below. # # Here's some sample output for "test/hledgeruicapture -f examples/sample.journal" # showing the initial output (accounts screen) then the outputs from four inputs: # RIGHT (draws register screen) # UP (moves selection up one row; only the heading and old/new rows are redrawn) # LEFT (draws accounts screen again) # 1 (redraws accounts screen with depth limit 1). # Note, # - only lines which have been redrawn are shown (and perhaps redrawing parts of lines is also possible) # - all inputs above cause the top line to be redrawn, which helps distinguish the outputs # - the top line's border chars are converted to --- # - empty lines and the bottom line (help & border) are omitted # # --- sample.journal account balances (1/8) # assets:bank:checking 0 # assets:bank:saving $1 # assets:cash $-2 # expenses:food $1 # expenses:supplies $1 # income:gifts $-1 # income:salary $-1 # liabilities:debts $1 # --- assets:bank:checking transactions (4/4) # 2008-01-01 income in:salary $1 $1 # 2008-06-01 gift in:gifts $1 $2 # 2008-06-02 save as:ba:saving $-1 $1 # 2008-12-31 * pay off li:debts $-1 0 # --- assets:bank:checking transactions (3/4) # 2008-06-02 save as:ba:saving $-1 $1 # 2008-12-31 * pay off li:debts $-1 0 # --- sample.journal account balances (1/8) # assets:bank:checking 0 # assets:bank:saving $1 # assets:cash $-2 # expenses:food $1 # expenses:supplies $1 # income:gifts $-1 # income:salary $-1 # liabilities:debts $1 # --- sample.journal account balances to depth 1 (1/4) # assets $-1 # expenses $2 # income $-2 # liabilities $1 # # # Todo: # - allow a sequence of inputs to be provided. Eg, first line of stdin # is hledger-ui arguments, and each following line is an input. # # # Requirements: # - empty (http://empty.sourceforge.net) is used as a (complicated, # delicate) way of running hledger-ui from a non-interactive script. # It is included in tools/empty/ and should be installed in PATH. It # seemed simpler than using expect (but maybe isn't). # For debugging: # watch -n0.2 "pgrep -fl '(empty|hledger-ui)' | grep -v watch" # https://www.linuxjournal.com/article/2156 # Convert a stream of hledger-ui ANSI output captured by empty # to a plain text sequence of outputs, suitable for tests. # Based on https://unix.stackexchange.com/a/4529/286158, Term::ANSIColor # First regex: # 1. convert "move to first column" escape sequences to newlines # 2. strip remaining escape sequences # 3. (replace empty's <<< delimiters with newlines - not any more, where have they gone ?) # 4. add a final newline # Second regex (because I couldn't fix newline matching in the first): # 1. replace screen-width dash borders with short hyphen delimiters # 2. strip trailing spaces on each line # 3. hide empty lines # 4. hide each screen's bottom help line hledgerui2txt() { perl -pe 's/\e\[\d+;1H/\n/g; s/\e\[?.*?[\@-~]//g; s/$/\n/;' | \ perl -ne 's/(─)+/---/; s/(─)+//; s/ +$//; print unless /^$/ or /q:quit/' } # Be sure not to leave empty and/or hledger-ui running if this script is killed. # (Doesn't prevent occasional "Fatal open FIFO ... No such file or directory" errors.) cleanup() { (echo; echo "hledgeruicapture: interrupted, cleaning up") >&2 if [[ -e p$$ ]]; then PID=$(cat p$$); kill $PID; rm -f c$$; fi } trap cleanup INT QUIT TERM # Start hledger-ui (and empty's monitor process) in the background, # specifying input/output FIFOs (with process-specific names in case # of concurrent testing). We don't use empty's default FIFOs because # finding their paths in a script is a hassle. empty -f -i i$$ -o o$$ -p p$$ hledger-ui "$@" # Wait for the first screen's final line (containing "q:quit"), then # send 'q' causing hledger-ui to exit. # Doesn't work if the line has already been displayed when this runs. #empty -w -t 1 -i o$$ -o i$$ q:quit q # Wait long enough to ensure the first screen has rendered # (assuming no crazy large data is being displayed). sleep 0.1 # # Testing: send some other stuff # U=$'\e[A' # D=$'\e[B' # R=$'\e[C' # L=$'\e[D' # empty -s -o i$$ $R; sleep 0.1 # empty -s -o i$$ $U; sleep 0.1 # empty -s -o i$$ $L; sleep 0.1 # empty -s -o i$$ 1; sleep 0.1 # empty -s -o i$$ 2; sleep 0.1 # empty -s -o i$$ 3; sleep 0.1 # empty -s -o i$$ 1; sleep 0.1 # capture the output FIFO's content in a file, before hledger-ui quits ? # hangs until ctrl-c, why ? #cat o$$ #> c$$ # Send 'q' on the input FIFO, causing hledger-ui to exit. empty -s -o i$$ q # But wait until after the pipe has been read, below ? Doesn't help. # (sleep 1; empty -s -o i$$ q) & # drain the output FIFO one last time, allowing empty monitor to exit ? no #cat o$$ # kill empty's monitor process deliberately ? no #empty -k `cat p$$` # Read all from the output FIFO, causing empty's monitor to exit and # clean up the FIFOs. And convert it to plain text. # can never get empty -r to work right ##empty -r -b 10000 -t 10 -i o$$ | hledgerui2txt # cat works.. # but only when running interactively. When non-interactive # (eg echo | ./uitest), the FIFO returns nothing. Could be "If all # file descriptors referring to the write end of a pipe have been # closed, then an attempt to read(2) from the pipe will see # end-of-file (read(2) will return 0)" cat o$$ | hledgerui2txt # convert the captured output, and clean up # cat c$$ | hledgerui2txt # rm c$$