2020-11-13 18:41:14 +00:00
# Terminal emulators all present a pretty similar interface.
# That gives us an opportunity to easily test their basic functionality with a single codebase.
#
# There are two tests run on each terminal emulator
# - can it successfully execute a command passed on the cmdline?
# - can it successfully display a colour?
# the latter is used as a proxy for "can it display text?", without going through all the intricacies of OCR.
#
# 256-colour terminal mode is used to display the test colour, since it has a universally-applicable palette (unlike 8- and 16- colour, where the colours are implementation-defined), and it is widely supported (unlike 24-bit colour).
#
# Future work:
# - Wayland support (both for testing the existing terminals, and for testing wayland-only terminals like foot and havoc)
# - Test keyboard input? (skipped for now, to eliminate the possibility of race conditions and focus issues)
{ system ? builtins . currentSystem ,
config ? { } ,
pkgs ? import ../.. { inherit system config ; }
} :
with import ../lib/testing-python.nix { inherit system pkgs ; } ;
with pkgs . lib ;
let tests = {
alacritty . pkg = p : p . alacritty ;
contour . pkg = p : p . contour ;
contour . cmd = " c o n t o u r $ c o m m a n d " ;
cool-retro-term . pkg = p : p . cool-retro-term ;
cool-retro-term . colourTest = false ; # broken by gloss effect
ctx . pkg = p : p . ctx ;
ctx . pinkValue = " # F E 0 0 6 5 " ;
darktile . pkg = p : p . darktile ;
eterm . pkg = p : p . eterm ;
eterm . executable = " E t e r m " ;
eterm . pinkValue = " # D 4 0 0 5 5 " ;
germinal . pkg = p : p . germinal ;
gnome-terminal . pkg = p : p . gnome . gnome-terminal ;
guake . pkg = p : p . guake ;
guake . cmd = " S H E L L = $ c o m m a n d g u a k e - - s h o w " ;
guake . kill = true ;
hyper . pkg = p : p . hyper ;
kermit . pkg = p : p . kermit-terminal ;
kgx . pkg = p : p . kgx ;
kgx . cmd = " k g x - e $ c o m m a n d " ;
kgx . kill = true ;
kitty . pkg = p : p . kitty ;
kitty . cmd = " k i t t y $ c o m m a n d " ;
konsole . pkg = p : p . plasma5Packages . konsole ;
lxterminal . pkg = p : p . lxterminal ;
mate-terminal . pkg = p : p . mate . mate-terminal ;
mate-terminal . cmd = " S H E L L = $ c o m m a n d m a t e - t e r m i n a l - - d i s a b l e - f a c t o r y " ; # factory mode uses dbus, and we don't have a proper dbus session set up
mlterm . pkg = p : p . mlterm ;
mrxvt . pkg = p : p . mrxvt ;
qterminal . pkg = p : p . lxqt . qterminal ;
qterminal . kill = true ;
roxterm . pkg = p : p . roxterm ;
roxterm . cmd = " r o x t e r m - e $ c o m m a n d " ;
sakura . pkg = p : p . sakura ;
st . pkg = p : p . st ;
2022-03-18 07:14:24 +00:00
st . kill = true ;
2020-11-13 18:41:14 +00:00
stupidterm . pkg = p : p . stupidterm ;
stupidterm . cmd = " s t u p i d t e r m - - $ c o m m a n d " ;
terminator . pkg = p : p . terminator ;
terminator . cmd = " t e r m i n a t o r - e $ c o m m a n d " ;
terminology . pkg = p : p . enlightenment . terminology ;
terminology . cmd = " S H E L L = $ c o m m a n d t e r m i n o l o g y - - n o - w i z a r d = t r u e " ;
terminology . colourTest = false ; # broken by gloss effect
termite . pkg = p : p . termite ;
termonad . pkg = p : p . termonad ;
tilda . pkg = p : p . tilda ;
tilix . pkg = p : p . tilix ;
tilix . cmd = " t i l i x - e $ c o m m a n d " ;
urxvt . pkg = p : p . rxvt-unicode ;
wayst . pkg = p : p . wayst ;
wayst . pinkValue = " # F F 0 0 6 6 " ;
wezterm . pkg = p : p . wezterm ;
xfce4-terminal . pkg = p : p . xfce . xfce4-terminal ;
xterm . pkg = p : p . xterm ;
} ;
in mapAttrs ( name : { pkg , executable ? name , cmd ? " S H E L L = $ c o m m a n d ${ executable } " , colourTest ? true , pinkValue ? " # F F 0 0 8 7 " , kill ? false }: makeTest
{
name = " t e r m i n a l - e m u l a t o r - ${ name } " ;
2022-03-23 11:19:15 +00:00
meta = with pkgs . lib . maintainers ; {
2020-11-13 18:41:14 +00:00
maintainers = [ jjjollyjim ] ;
} ;
machine = { pkgsInner , . . . }:
{
imports = [ ./common/x11.nix ./common/user-account.nix ] ;
# Hyper (and any other electron-based terminals) won't run as root
test-support . displayManager . auto . user = " a l i c e " ;
environment . systemPackages = [
( pkg pkgs )
( pkgs . writeShellScriptBin " r e p o r t - s u c c e s s " ''
echo 1 > /tmp/term-ran-successfully
$ { optionalString kill " p k i l l ${ executable } " }
'' )
( pkgs . writeShellScriptBin " d i s p l a y - c o l o u r " ''
# A 256-colour background colour code for pink, then spaces.
#
# Background is used rather than foreground to minimize the effect of anti-aliasing.
#
# Keep adding more in case the window is partially offscreen to the left or requires
# a change to correctly redraw after initialising the window (as with ctx).
while :
do
echo - ne " \e [ 4 8 ; 5 ; 1 9 8 m "
sleep 0 .5
done
sleep infinity
'' )
( pkgs . writeShellScriptBin " r u n - i n - t h i s - t e r m " " s u d o - u a l i c e r u n - i n - t h i s - t e r m - w r a p p e d $ 1 " )
( pkgs . writeShellScriptBin " r u n - i n - t h i s - t e r m - w r a p p e d " " c o m m a n d = \" $ ( w h i c h \" $ 1 \" ) \" ; ${ cmd } " )
] ;
# Helpful reminder to add this test to passthru.tests
warnings = if ! ( ( pkg pkgs ) ? " p a s s t h r u " && ( pkg pkgs ) . passthru ? " t e s t s " ) then [ " T h e p a c k a g e f o r ${ name } d o e s n ' t h a v e a p a s s t h r u . t e s t s " ] else [ ] ;
} ;
# We need imagemagick, though not tesseract
enableOCR = true ;
testScript = { nodes , . . . }: let
in ''
with subtest ( " w a i t f o r x " ) :
start_all ( )
machine . wait_for_x ( )
with subtest ( " h a v e t h e t e r m i n a l r u n a c o m m a n d " ) :
# We run this command synchronously, so we can be certain the exit codes are happy
machine . ${ if kill then " e x e c u t e " else " s u c c e e d " } ( " r u n - i n - t h i s - t e r m r e p o r t - s u c c e s s " )
machine . wait_for_file ( " / t m p / t e r m - r a n - s u c c e s s f u l l y " )
$ { optionalString colourTest ''
import tempfile
import subprocess
def check_for_pink ( final = False ) -> bool :
with tempfile . NamedTemporaryFile ( ) as tmpin :
machine . send_monitor_command ( " s c r e e n d u m p { } " . format ( tmpin . name ) )
cmd = ' convert { } - define histogram:unique-colors=true - format " % c " histogram:info:'.format (
tmpin . name
)
ret = subprocess . run ( cmd , shell = True , capture_output = True )
if ret . returncode != 0 :
raise Exception (
" i m a g e a n a l y s i s f a i l e d w i t h e x i t c o d e { } " . format ( ret . returncode )
)
text = ret . stdout . decode ( " u t f - 8 " )
return " ${ pinkValue } " in text
with subtest ( " e n s u r i n g n o p i n k i s p r e s e n t w i t h o u t t h e t e r m i n a l " ) :
assert (
check_for_pink ( ) == False
) , " P i n k w a s p r e s e n t o n t h e s c r e e n b e f o r e w e e v e n l a u n c h e d a t e r m i n a l ! "
with subtest ( " h a v e t h e t e r m i n a l d i s p l a y a c o l o u r " ) :
# We run this command in the background
2022-06-13 04:23:19 +01:00
assert machine . shell is not None
2020-11-13 18:41:14 +00:00
machine . shell . send ( b " ( r u n - i n - t h i s - t e r m d i s p l a y - c o l o u r | & s y s t e m d - c a t - t t e r m i n a l ) & \n " )
with machine . nested ( " W a i t i n g f o r t h e s c r e e n t o h a v e p i n k o n i t : " ) :
retry ( check_for_pink )
'' } '' ;
}
) tests