| #!/usr/bin/env lua |
| |
| local VISUALDELAY = os.getenv("VISUALDELAY") |
| |
| local visual = VISUALDELAY or false |
| local visual_delay = VISUALDELAY and (tonumber(VISUALDELAY)) or 0.1 |
| |
| local signal = require("posix.signal") |
| local unistd = require("posix.unistd") |
| local time = require("posix.time") |
| local curses = require("posix.curses") |
| local rote = require("rote") |
| |
| local rt = rote.RoteTerm(24, 80) |
| |
| os.execute("make coverage") |
| os.execute("rm -f *.gcda */*.gcda") |
| os.execute("rm -f coverage.info test.htoprc") |
| os.execute("rm -rf lcov") |
| |
| rt:forkPty("HTOPRC=./test.htoprc ./htop") |
| |
| local stdscr, term_win |
| -- Curses initalization needed even when not in visual mode |
| -- because luaposix only initializes KEY_* constants after initscr(). |
| stdscr = curses.initscr() |
| if visual then |
| curses.echo(false) |
| curses.start_color() |
| curses.raw(true) |
| curses.halfdelay(1) |
| stdscr:keypad(true) |
| term_win = curses.newwin(24, 80, 0, 0) |
| local function makePair(foreground, background) |
| return background * 8 + 7 - foreground |
| end |
| -- initialize the color pairs the way rt:draw() expects it |
| for foreground = 0, 7 do |
| for background = 0, 7 do |
| if foreground ~= 7 or background ~= 0 then |
| local pair = makePair(foreground, background) |
| curses.init_pair(pair, foreground, background) |
| end |
| end |
| end |
| else |
| curses.endwin() |
| end |
| |
| local function delay(t) |
| time.nanosleep({ tv_sec = math.floor(t), tv_nsec = (t - math.floor(t)) * 1000000000 }) |
| end |
| |
| local function show(key) |
| rt:update() |
| if visual then |
| rt:draw(term_win, 0, 0) |
| if key then |
| term_win:mvaddstr(0, 0, tostring(key)) |
| end |
| term_win:refresh() |
| |
| delay(visual_delay) |
| end |
| end |
| |
| local function send(key, times) |
| for i = 1, times or 1 do |
| delay(0.003) -- 30ms delay to avoid clobbering Esc sequences |
| if type(key) == "string" then |
| for c in key:gmatch('.') do |
| rt:keyPress(string.byte(c)) |
| end |
| else |
| rt:keyPress(key) |
| end |
| show(key) |
| end |
| end |
| |
| local function string_at(x, y, len) |
| rt:update() |
| local out = {} |
| for i = 1, len do |
| out[#out+1] = rt:cellChar(y-1, x+i-2) |
| end |
| return table.concat(out) |
| end |
| |
| local function is_string_at(x, y, str) |
| return string_at(x, y, #str) == str |
| end |
| |
| local function check_string_at(x, y, str) |
| return { str, string_at(x, y, #str) } |
| end |
| |
| local ESC = 27 |
| |
| time.nanosleep({ tv_sec = 0, tv_nsec = 150000000 }) -- give some time for htop to initialize. |
| |
| local pos_panelhdr = (function() |
| for i = 1, 24 do |
| if is_string_at(3, i, "PID") then |
| return i |
| end |
| end |
| end)() or 1 |
| |
| show() |
| |
| local terminated = false |
| signal.signal(signal.SIGCHLD, function(_) |
| terminated = true |
| end) |
| |
| local function running_it(desc, fn) |
| it(desc, function() |
| assert(not terminated) |
| show() |
| fn() |
| assert(not terminated) |
| end) |
| end |
| |
| local function check(t) |
| return t[1], t[2] |
| end |
| |
| describe("htop test suite", function() |
| running_it("visits each setup screen", function() |
| send("S") |
| send(curses.KEY_DOWN, 3) |
| send(curses.KEY_F10) |
| end) |
| running_it("adds and removes PPID column", function() |
| send("S") |
| send(curses.KEY_DOWN, 3) |
| send(curses.KEY_RIGHT, 2) |
| send(curses.KEY_DOWN, 2) |
| send("\n") |
| send(curses.KEY_F10) |
| delay(0.2) |
| local ppid = check_string_at(2, pos_panelhdr, "PPID") |
| send("S") |
| send(curses.KEY_DOWN, 3) |
| send(curses.KEY_RIGHT, 1) |
| send(curses.KEY_DC) |
| send(curses.KEY_F10) |
| delay(0.2) |
| local not_ppid = check_string_at(2, pos_panelhdr, "PPID") |
| assert.equal(check(ppid)) |
| assert.not_equal(check(not_ppid)) |
| end) |
| running_it("changes CPU affinity for a process", function() |
| send("a") |
| send(" \n") |
| send(ESC) |
| end) |
| running_it("adds and removes a clock widget", function() |
| send("S") |
| send(curses.KEY_RIGHT, 3) |
| send("\n") |
| send(curses.KEY_UP, 4) |
| send("\n") |
| local time = check_string_at(41, 2, "Time") |
| send(curses.KEY_DC) |
| delay(0.3) |
| local not_time = check_string_at(41, 2, "Time") |
| send(ESC) |
| assert.equal(check(time)) |
| assert.not_equal(check(not_time)) |
| end) |
| running_it("adds a hostname widget", function() |
| send("S") |
| send(curses.KEY_RIGHT, 3) |
| send(curses.KEY_DOWN, 8) |
| send("\n") |
| send("\n") |
| send(ESC) |
| end) |
| it("finally quits", function() |
| assert(not terminated) |
| send("q") |
| while not terminated do |
| unistd.sleep(1) |
| end |
| assert(terminated) |
| if visual then |
| curses.endwin() |
| end |
| os.execute("make lcov && xdg-open lcov/index.html") |
| end) |
| end) |
| |