Skip to content

Instantly share code, notes, and snippets.

@twolfson
Last active November 13, 2018 04:57
Show Gist options
  • Save twolfson/e24af2d3d5424540492f to your computer and use it in GitHub Desktop.
Save twolfson/e24af2d3d5424540492f to your computer and use it in GitHub Desktop.
Proof of concept to see how testing with ncurses works
# https://docs.python.org/2/howto/curses.html
# Load in our dependencies
import curses
import sys
# Notes for future self:
# KeyboardInterrupt is generated by using an actual terminal
# Ctrl+C is interpretted into a signal
# We can actually type Ctrl+C via the `echo` command or by using `curses'` raw mode
# http://linux.die.net/man/3/cbreak
# We are running into a subprocess issue of testing `curses`
# I think it's likely easier to test the inner workings of the program via an API than to test its entirety
# Maybe it's possible to abstract the terminal integration to a very thin layer
# Which can be indepdendently tested
# Define our main function
def main():
# Initialize our screen
# https://github.com/python-git/python/blob/715a6e5035bb21ac49382772076ec4c630d6e960/Lib/curses/__init__.py#L32-L33
from StringIO import StringIO
sys.stdout = StringIO()
stdscr = curses.initscr()
pad = curses.newpad(100, 100)
# These loops fill the pad with letters; this is
# explained in the next section
for y in range(0, 100):
for x in range(0, 100):
try:
pad.addch(y, x, ord('a') + (x*x+y*y) % 26)
except curses.error:
pass
# Displays a section of the pad in the middle of the screen
pad.refresh(0, 0, 5, 5, 20, 75)
# Wait for someone to kill our script
while 1:
try:
pass
except KeyboardInterrupt:
curses.endwin()
sys.exit(0)
# If we are being invoked directly, call main
if __name__ == '__main__':
main()
# Load in our dependencies
import subprocess
import time
# Define constants
SLEEP_INTERVAL = 0.1
# Define our main function
def main():
# Open files to output content to
STDOUT_PATH = '/tmp/gist-test-ncurses/stdout'
stdout_fd = open(STDOUT_PATH, 'w+')
stderr_fd = open('/tmp/gist-test-ncurses/stderr', 'w+')
# Start our child process
PIPE = subprocess.PIPE
child = subprocess.Popen(['python', 'main.py'], stdin=PIPE, stdout=stdout_fd, stderr=stderr_fd)
# Verify our process is running
print 'running', child.poll()
# Link with claims ofexperience but no idea...
# http://stackoverflow.com/questions/5579901/automated-test-tools-for-linux-ncurses
# Wait for output from our program
last_tell = 0
time_since_last_output = 0.0
def fn_loop(last_tell, time_since_last_output):
tell = stdout_fd.tell()
print 'wat', tell
if last_tell != tell:
print 'tmp_stdout', tell
last_tell = tell
else:
# If we have waited for 1 second, stop
if time_since_last_output > 1.0:
return
# Otherwise, sleep for a fraction of a second
time_since_last_output += SLEEP_INTERVAL
print 'sleeping for', SLEEP_INTERVAL
time.sleep(SLEEP_INTERVAL)
fn_loop(last_tell, time_since_last_output)
fn_loop(last_tell, time_since_last_output)
# print 'stderr', child.stderr.readline()
# Kill the process
# child.kill()
# print 'waiting for exit', child.wait()
# Close our files
stdout_fd.close()
stderr_fd.close()
# If we are being invoked directly, call main
if __name__ == '__main__':
main()
@richrd
Copy link

richrd commented Oct 7, 2015

+1 to these:

# I think it's likely easier to test the inner workings of the program via an API than to test its entirety
# Maybe it's possible to abstract the terminal integration to a very thin layer
# Which can be indepdendently tested

Edit:
I've actually been pondering that one day it would be great to have all the UI logic entirely separate from curses. That way we could switch to other terminal libraries or even some really basic desktop GUI (if needed). I used curses mainly since it's available by default and that's important for me. (I'm even running Suplemon on some 'legacy' servers with Python 2.6)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment