1028 lines · 48.5 KB · Text
RawDownload
#+TITLE: Zutty developer guide#+OPTIONS: author:nil timestamp:nil num:nil val:nil html-style:nil H:3 ^:{}#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="org.css"/> * Hacking on Zutty** Compiling for development or debugging If you plan to spend time hacking on Zutty, you should first compile adebug build of it. The purpose of this is to compile in trace loggingoutput, as well as debugging facilities. To configure the build for this development mode, add the option=--debug= to the configuration: : ./waf configure --debug Feel free to add any other options you usually use for the normalbuild. Confirm that you see this line in the output: : Debug build                              : yes Then, compile Zutty as usual: : ./waf [-j <JOBS>] The build will produce an executable named =zutty.dbg= (as opposed to=zutty= for normal builds). This allows you to keep a normal build anda debug build from the same source side by side each other, whichmight prove useful while working on some debugging-intensive problem. Normally, you will not want to install the development buildsystem-wide. Run it from an existing terminal as =build/src/zutty.dbg-v= (plus any other options) to receive verbose debugging logs in theconsole. As stated in the [[./USAGE.org][User guide]], by default (if neither =-quiet= nor=-verbose= is given), Zutty will print errors and warnings, but notinfo messages. Those have to be enabled with =-verbose=, and in caseof a debug build, that will enable Trace messages as well. ** The Step Debugger A debug build of Zutty has compiled-in support not just for generatinglots of trace output, but also for a facility called the StepDebugger. This is a facility specifically built to enable closeinspection of the virtual terminal in action. The Step Debugger is initially dormant; it can be activated bypressing the "debug key", which is hardwired to PrintScreen (but thehook for it is only compiled in the debug build, so it does not affectthe PrintScreen key in a normal build). When the Step Debugger is activated, the Zutty process will suspenditself on the completion of every N-th escape sequence (i.e., when thestate machine is set back to "normal text input"), with N cycledthrough 1, 10, 100 and zero (off) on each additional press of thePrintScreen key. Move focus to the Zutty window, press PrintScreen, and confirm thatyou see this line in the log output: : T [vterm.icc: 40] *** DEBUG step=1 The =step=1= communicates that going forward, Zutty will suspenditself on every completed escape sequence. To test this, typesomething (I am typing the letter =k=) and then hit Backspace. Thiswill trigger the Debug Stop, and the log will look similar to this: : T [vterm.icc:366] pty write (mod=0): 'k' (1 bytes): T [vterm.icc:400] pty read: 'k' (1 bytes): T [vterm.icc:617] hideCursor []         p(0,48)  d(42,103)  mgn[0,42)  hmgn:0 [0,103): T [vterm.icc:108] Inserted: 'k' (1 bytes): T [vterm.icc:605] showCursor []         p(0,49)  d(42,103)  mgn[0,42)  hmgn:0 [0,103): T [vterm.icc:389] pty write: '\x7f' (1 bytes): T [vterm.icc:400] pty read: '\b\e[K' (4 bytes): T [vterm.icc:617] hideCursor []         p(0,49)  d(42,103)  mgn[0,42)  hmgn:0 [0,103): T [vterm.icc:914] csi_CUB []    p(0,49)  d(42,103)  mgn[0,42)  hmgn:0 [0,103): T [vterm.icc:1144] csi_EL [0]   p(0,48)  d(42,103)  mgn[0,42)  hmgn:0 [0,103): T [vterm.icc: 50] *** DEBUG STOP (step=1), 4 bytes since last::         '\b\e[K' (4 bytes): T [vterm.icc: 55] Issue 'kill -CONT 5429' or 'fg' to continue. Following the trace log, we can see that first the keypress was sentto the shell (=pty write=), then the echoed character was read backfrom the shell (=pty read=), which triggered an insertion of =k= astext. The screen-updating is surrounded by hiding and showing thecursor. Then, as a consequence of the Backspace keypress, Zutty sendsthe =DEL= character (=\x7f=) to the shell, which echoes back =\b\e[K=:first =\b=, a backspace character, followed by =\e[K=, or a "CSI K"sequence in VT100-speak, which instructs Zutty to clear the terminalscreen from the cursor to the end of line. Since this constitutes afinished escape sequence, this is the point where the Debug Stopoccurs. As part of processing the input stream from the terminal, Zuttyexecuted the functions =csi_CUB= (interpreting =\b=) and =csi_EL=(interpreting =\e[K=). The former stands for Cursor Back, the latterfor Erase Line -- these are the names of the requested screenoperations according to the VT100-series standards. The argumentsparsed from the input escape sequences, effective at the time of thefunction call, are visible in square brackets. The single argumentzero (shown as =[0]=) passed to the Erase Line routine is the implicitdefault; the actual escape sequence did not contain a numericargument. Also worth noting is that on each trace point (such as =hideCursor=,=showCursor=, =csi_CUB=, etc.) several state variables are printed.These include the current cursor position =(0,49)=, the dimensions=(42,103)=, and the scrolling area top and bottom margins =[0,42)=(the mismatched brackets denote that the top row of scrolling isinclusive of the scrolling range, while the bottom row is exclusive). To resume Zutty, send a continue signal (SIGCONT) via kill, or justtype =fg= into the terminal running Zutty. The latter is a neat trickthat relies on the parent terminal detecting that the child processhas been stopped, and putting it into the background. When trying to understand how the virtual terminal processes a longerinput sequence, it becomes tedious to step through each escapesequence one by one. Pressing PrintScreen will cycle through thenumber of debug steps to go between each stop: first 1, then 10, 100,then 0 (the Step Debugger is turned off), then back to 1, 10, 100,then off again, etc. While doing this, you will see this in the outputlog: : T [vterm.icc: 40] *** DEBUG step=10: T [vterm.icc: 40] *** DEBUG step=100: T [vterm.icc: 40] *** DEBUG step=0: T [vterm.icc: 40] *** DEBUG step=1: T [vterm.icc: 40] *** DEBUG step=10: T [vterm.icc: 40] *** DEBUG step=100: ... ** Automated testing By their very nature, graphical terminal emulators are interactiveprograms. Therefore, testing them in an automated fashion (e.g., forregression testing) can be tricky. We employ a method suitable as a general means to automation, as it isindependent of the terminal under test: it does not require modifyingthe program by e.g., implementing test hooks to inject events or reportscreen content. This allows us to include several established terminalemulators in the test along with Zutty to research the state of theart and see how different programs stack up against each other. Prerequisites:- imagemagick (for convert & identify), wmctrl, xvkbd  : apt-get install imagemagick wmctrl xvkbd On a high level, testing consists of these steps:- Start the terminal (the unit under test) as a subprocess and note  its pid- Obtain its X window id:  : wmctrl -lp | grep <pid> | awk '{print $1}'- use =xvkbd= to send events to the window:  : xvkbd -window <id> -no-jump-pointer -text "\D3\{+1}\D3\{-1}\D3\{+Return}\D3\{-Return}\D3"  Note the explicit keysym presses and releases, plus the interleaved delays.  For reference, see: http://t-sato.in.coocan.jp/xvkbd/- Make a screenshot of the window via the window id  : xwd -nobdrs -id <id> | convert xwd:- png:- > <shot-name>.png- Generate a signature of the screen content, to be compared against a reference value:  : identify -verbose <shot-name>.png | grep signature | awk '{print $2}' | cut -32  (We cut the hash in half to make it less unwieldy.)  Having consistent hashes that depend only on the rendered pixel  image is convenient, as we do not need to store the reference images  themselves beside our test script. The above steps are automated by some fairly straightforward bashscripts under the =test/= subdirectory. These scripts all source the=testbase.sh= script, which constitutes the test library. *** CAVEATs While Zutty itself compiles and runs on Linux as well as BSDplatforms, the test toolkit assumes a specific development environment(most notably: Linux platform with =bash= as the default shell, andthe presence of a window manager with certain standard capabilities). Please also note that the reference signatures of screen captures arevalid for ImageMagick version 6 only; version 7 has altered the imagesignature hash algorithm, which breaks the hashes included in thetests. *** Anatomy of a test script Each executable script under =test/= is an individually runnable testsuite. It is written as a plain old bash script, sourcing the=testbase.sh= test code library and using its facilities. For example,=truecolor.sh= is a very simple test script to test support for settingcolor attributes to truecolor (24 bit) values. The full script isreproduced below: #+BEGIN_SRC sh#!/usr/bin/env bash cd $(dirname $0)source testbase.sh IN "source truecolor_inc.sh\r"SNAP truecolor_01 33a31e4d3b9fbe486c27b01764dc1823#+END_SRC The script starts by declaring itself as a shell script, then settingup its working directory to be the location of the script (aconvenience to make relative file paths work in later parts of thescript, independent of the location the script was invoked from). Then=testbase.sh= is sourced. The actual test code is just two lines, starting with the commands=IN= and =SNAP=. These are invocations of functions defined in=testbase.sh= and execute in the environment set up by sourcing thatfile. =IN= will send the specified string, as keyboard input, to theterminal under test. In our example, the shell running in the terminalwill source the file =truecolor_inc.sh= that contains some setup code(not reproduced here) to make a certain pattern appear on theterminal. Note the trailing =\r= that will result in a virtual =Enter=keypress. The subsequent =SNAP= will capture the resulting terminal windowcontent under the name =truecolor_01=, generate a signature (hash) ofit, and compare that with the supplied value. If there is a match, theoutput is verified to be correct; else a test failure is reported. The snap name is used to save the captured window image under=test/output/<profile>=.  This is useful for later inspection of testresults. With the default profile (see [[Test profiles]] below), theoutput of this test will be saved as=test/output/zutty/truecolor_01.png=. There are some other useful functions exported by the test framework,e.g.: =CHECK_DEPS=, =CHECK_FILES=, and =WAIT_FOR_DOT_COMPLETE=. If youencounter them in test script code, it is best to look directly in=testbase.sh= for their implementation. Note that starting and stopping of the terminal under test is done aspart of the test framework and nothing is explicitly written in thetest script. See [[Test profiles]] below on how to control the details ofthis process. *** Common test script options Test scripts similar to the one shown above (building on=testbase.sh=) all take a uniform set of command line options. Allarguments are optional, below defaults are in effect for omitted ones.Syntax: =--<arg-name>=<arg-value>=; value defaults to "yes" ifomitted. :   Option             Default:   --------------------------:   --ci-mode          no:   --debug            no:   --profile          zutty:   --step             no:   --update-sig       no **** =--step= The =--step= option can be given without argument, in which case itwill be equivalent to =--step=yes=, or given as =--step=new=.  Theformer one will result in step mode, which will pause immediatelyafter each snapshot is taken (the terminal under test still displayingthis output, allowing visual inspection), and display a prompt: : [S]tep / [N]ew only / [C]ontinue / [Q]uit (s/n/c/q) ? This allows the user to choose how to proceed:- *S* - continue stepping, i.e., stop after each snapshot- *N* - continue without stopping, except on new snapshots- *C* - continue without ever stopping again- *Q* - quit the test The =new= option is useful when developing a test suite. It will runthe test script forward until a =SNAP= command without a verificationhash is found. **** =--update-sig= Enabling =--update-sig= will result in a prompt on a verificationfailure, i.e., when the =SNAP= command captures a screenshot with adifferent hash than the reference stored in the script: : Update signature: [y]es / [N]o / [a]ll ? By answering *Y* here, the signature in the script will be updated.By answering *A*, all future differences will also be updated withouta further prompt. This is useful in case the behaviour of Zutty ischanged in a way that alters its output; in case it is establishedthat the new output is "more correct" than the previous one; and wewant to adapt the tests to verify against this new output in thefuture. Use this with a great deal of caution. It is recommended to use *Y* infavour of *A* and before answering each prompt, to do a careful visualinspection of each screen for correctness. **** =--profile= See [[Test profiles]] below. **** =--ci-mode= The =--ci-mode= option sets up the test script to execute in anunattended manner, suitable for automated testing. Step mode is turnedoff (overriding =--step=), signature updates are turned off(overriding =--update-sig=), and the script is set up to immediatelyexit with a nonzero code on a verification failure. **** =--debug= The =--debug= option sets the variable =DEBUG= to =yes= (instead ofits default value =no=). This is used by the =zutty= test profile toensure that the correct UUT executable (=zutty= vs. =zutty.dbg=) islaunched. This option is currently not used by any other profile, norfor any other, more general purpose. *** Correctness tests The list of correctness tests (automatically run in sequence by[[The CI test script]]): - =keys.sh=: Keyboard input handling (see [[./KEYS.org][Key mapping sequences]] for  further documentation). Note: This test might fail if your computer  is configured to use a non-US keyboard.- =nonascii.sh=: Test displaying non-ascii (double-width and other  exotic) characters, based on the Emacs 'hello' file  (=M-x view-hello-file=)- =scrollback.sh=: Scrollback (page history) support- =title.sh=: Setting the window title from within the terminal via  escape sequences- =truecolor.sh=: True color support- =utf8.sh=: UTF-8 support (based on the  [[../test/UTF-8-test.txt][UTF-8 decoder capability and stress test]] by Markus Kuhn)- =vttest.sh=: VTTEST screens. Note: This suite depends on a specific  version of =vttest=, and will complain if the version found does not  match. Just run the vttest install script mentioned by the error  message, and you should be good to go.- =wraptest.sh=: Additional tests for verifying the correctness of  last-column/pending-wrap subtleties. Note: This suite depends on the  [[https://github.com/mattiase/wraptest][wraptest]] program (not bundled with the zutty source repository).  Just run the install script mentioned by the error message you get  on the first run, and you should be good to go. Apart from running all the tests via [[The CI test script]] (which youshould routinely run during development, and especially before sendinga patch or opening a pull request), it is also possible to run any ofthe above tests manually. For example, to run the test automating atraversal of Vttest's menu system (and in case of running againstZutty, also verifying that results are as expected): : test/vttest.sh Do not forget about the [[Common test script options]] above; those becomeuseful during development (both of Zutty itself and the test suites).For example, to run the above test step-by-step (stopping at eachimage checkpoint): : test/vttest.sh --step *** Font rasterization tests A separate test =fonts.sh= exists that is very similar to thecorrectness tests above, but is not part of [[The CI test script]]. Thisrenders a captured hardcopy of a terminal-intensive program (BpyTOP)exercising many features (chiefly, Unicode line drawing glyphs) thatare helpful in spotting errors in the font loading and rasterizationprocess employed by Zutty, and validates the result across a matrix ofseveral font faces and sizes. Please read the commentary in the testscript for the fonts required to run the test. *** Performance tests The performance of Zutty can be verified with the below tests. Theseare to be run manually (similar to how you run any of the[[Correctness tests]] on an ad-hoc basis). The goal of these tests is toget a handle on performance under repeatable circumstances. Since everyone's hardware and systems are different, it does not maketoo much sense to compare numbers obtained by different people atdifferent times.  Rather, it is most useful to compare the resultsobtained on the same system (running the comparison tests after oneanother without any changes to the rest of the system), with the goalof comparing a proposed set of patches to the baseline of Zutty, or tocompare Zutty with another terminal emulator. Needless to say: when running the below scripts, your machine shouldbe otherwise idle. While correctness tests should ideally be run on both the debug build(do this by passing =--debug= to the test scripts) and the normalbuild, it does not make sense to run performance tests on the debugbuild. The results would be meaningless due to excessive logging andhitting other debug-only code paths. Current performance tests: - =cat_dict.sh=: Arguably the dumbest possible performance test of any  text terminal, this test consists of outputting a very long text  file containing (mostly) very short lines with English words, one  per line. This test will be repeated a number of times (check  =TIMES= in the script) and the overall timing and data throughput  will be computed at the end. Since the input does not contain any  terminal controls (escape sequences), it is a measure of the raw  incoming data rate the terminal can sustain while frequently forced  to scroll/page its output. This load resembles one extreme end of  the way a terminal can be used. - =cat_vtscript.sh=: This test generates load resembling the other  extreme end of possible terminal usage, by outputting the stream of  data written to the terminal in the course of the VTTEST cycle (see  =vttest.sh= among the [[Correctness tests]]). However, instead of  verifying the correctness of the generated screen output, here we  are interested in the performance of processing the input stream  heavy on all kinds of escape sequences. Screen updates are dominated  by intra-screen rewrites and relatively little scrolling/paging  activity is forced.  Similar to the =cat_dict.sh= test, the input is  fed into the terminal a number of times, and overall timing and  throughput is measured and calculated. *** The CI test script The script =test/run_ci.sh= will run all automated [[Correctness tests]]in sequence with the =--ci-mode= option, stopping and exiting with anonzero return code if any of them exits with an error, and concludingwith a confirmatory message and zero return code otherwise. You should always run this at a convenient time (it will occupy yourscreen for about 25 minutes) and observe the successful result beforesending a patch with your changes. For extra confidence, build Zuttyboth with and without debug, and run the CI script both with andwithout the =--debug= option. If you have changed anything related to font loading or rasterization(=font.cc= or =fontpack.cc=), make sure you run the [[Font rasterizationtests]] as well.  Please read the commentary in the test script for morecontext. *** Test profiles Given that our method of testing is independent of the terminal itself(meaning that we do not rely on any hooks or test instrumentation inthe terminal itself), we can run the same tests against otherterminals, too. This is useful for comparison and research purposes.The default profile is =zutty=, invoking the locally built Zuttyexecutable as the unit under test. Profiles are defined as shell include files under =test/profiles=, andcan be invoked by passing the =--profile= option to the testscript. For example, to run the VTTEST suite against =xterm=: : ./test/vttest.sh [--step] --profile=xterm Available profiles can be enumerated by looking in =test/profiles/=(pass their name without the =.sh= extension to =--profile=, asabove). The list is also printed by the test script in case an invalidprofile name is given. Each test profile contains a program invocation assigned to =UUT_EXE=,used to launch the terminal under test with the right arguments. Theterminal must be configured so that its geometry is 80 characters wideand 24 rows tall. Some other variables set up in the profile encode differentcapabilities to control the test scripts so that unsupported featuresare skipped (and the terminal does not encounter confusing escapesequences). Examples: =MISSING_ANSWERBACK=, =MISSING_DSR=,=MISSING_SECONDARY_DA=, =SUPPORTS_VT52=, =SUPPORTS_VT220=. Pleasecheck the script files for details. Note that validation against the stored image hashes does not makesense, unless preparations are made to ensure that the terminalsettings (size, font, etc.) are perfectly identical. Even then,differences from the valid output stored for Zutty do not necessarilyconstitute bugs in any other terminal, unless the rendered screencontent is visibly wrong. For this reason, auto-validation of thesaved screens runs in a relaxed mode for all profiles other than=zutty=. This means that for terminals other than Zutty, matches arestill prominently displayed (by a line reading =MATCH= in green),while non-matches are considered normal and indicated by =DONE=,followed by a metric of the image difference compared to thereference. The output image and the image generated for Zutty will bediffed, resulting in a difference image that will highlightdifferences with red. ** Contribution guide There is no commercial entity behind Zutty. It is a volunteer effortwith extremely limited resources. Your contributions will be dulyconsidered, but please discuss your plans with us before starting out.Otherwise, you run the risk of being ignored after having put time andeffort into the preparation of a patch. Not every aspect of what makes a good contribution can be codified (webelieve having good taste plays an important role), but as a minimum,please keep the following in mind: *** Respect existing coding style 

Lines 1–500 of 1028.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t