-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
automatic tests for TUI (ncurses) #251
Changes from 13 commits
a2d8479
12c144d
eee00b3
1b27612
1f99889
79c25ff
55a3d2b
b60f2bb
e2fa1a5
89b2eaf
7c7d193
50ba537
c660d1a
12eb453
d3f08eb
387c970
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ | |
/.yardoc | ||
/package/*.tar.* | ||
*.pot | ||
# screen captures from the libyui tests | ||
*.out.txt | ||
*.out.esc |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
------------------------------------------------------------------- | ||
Tue Oct 13 14:42:52 UTC 2020 - Martin Vidner <[email protected]> | ||
|
||
- Add automatic TUI (ncurses) tests using tmux (bsc#1165388). | ||
- 4.3.5 | ||
|
||
------------------------------------------------------------------- | ||
Thu Sep 24 19:46:00 UTC 2020 - [email protected] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
require_relative "rspec_tmux_tui" | ||
|
||
describe "Menu Item" do | ||
bug = "1177760" | ||
around(:each) do |ex| | ||
@base = "menu_hotkeys_#{bug}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is it used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in the screenshot filenames: |
||
|
||
yast_ncurses = "#{__dir__}/yast_ncurses" | ||
example_dir = "/usr/share/doc/packages/yast2-ycp-ui-bindings/examples" | ||
@tui = TmuxTui.new_session "#{yast_ncurses} #{example_dir}/MenuBar1.rb" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I expect this whole creation need to be redone in each test. So why not simply have helper in TmuxTui and have something like |
||
|
||
ex.run | ||
|
||
if @tui.has_session? | ||
@tui.kill_session | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why two methods? I would have just one like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NP: Maybe we should print a warning on STDERR in this case, each test tries to finish cleanly (by pressing "Quit" or "Close" button), if the test is still running then something went wrong. |
||
end | ||
end | ||
|
||
it "has hotkeys in menu items, boo##{bug}" do | ||
@tui.await(/File.*Edit.*View/) | ||
@tui.capture_pane_to("#{@base}-1-initial") | ||
|
||
@tui.send_keys "M-V" # &View | ||
@tui.capture_pane_to("#{@base}-2-view-menu-activated") | ||
|
||
@tui.send_keys "M-N" # &Normal | ||
@tui.capture_pane_to("#{@base}-3-normal-menu-item-activated") | ||
|
||
# the label | ||
expect(@tui.capture_pane).to include("Last Event") | ||
# the output | ||
expect(@tui.capture_pane).to include("view_normal") | ||
|
||
@tui.send_keys "M-Q" # &Quit | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
require "shellwords" | ||
|
||
class TmuxTui | ||
class Error < RuntimeError | ||
end | ||
|
||
def self.new_session(*args) | ||
new(*args) | ||
end | ||
|
||
attr_reader :session_name | ||
|
||
def initialize(shell_command, x: 80, y: 24, detach: true, session_name: nil) | ||
@shell_command = shell_command | ||
@x = x | ||
@y = y | ||
@detach = detach | ||
@session_name = session_name || new_session_name | ||
|
||
system "tmux", "new-session", | ||
"-s", @session_name, | ||
"-x", @x.to_s, | ||
"-y", @y.to_s, | ||
*(@detach ? ["-d"] : [] ), | ||
"sh", "-c", "#{@shell_command}; sleep 9999" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is that long sleep still needed? I think you can do in rspec specific expect name like |
||
end | ||
|
||
def new_session_name | ||
"tmux-tui-#{rand 10000}" | ||
end | ||
|
||
# @return [String] | ||
def capture_pane(color: false) | ||
esc = color ? "-e" : "" | ||
# FIXME: failure of the command? | ||
`tmux capture-pane -t #{session_name.shellescape} -p #{esc}` | ||
end | ||
|
||
def capture_pane_to(filename) | ||
txt = capture_pane(color: false) | ||
esc = capture_pane(color: true) | ||
File.write("#{filename}.out.txt", txt) | ||
File.write("#{filename}.out.esc", esc) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, as I understand it, there is no such thing as a raw terminal dump, unless we're talking about the linux console devices ( |
||
end | ||
|
||
def await(pattern) | ||
sleeps = [0.1, 0.2, 0.2, 0.5, 1, 2, 2, 5] | ||
txt = "" | ||
sleeps.each do |sl| | ||
txt = capture_pane | ||
case txt | ||
when pattern | ||
sleep 0.1 # draw the rest of the screen | ||
return | ||
else | ||
sleep sl | ||
end | ||
end | ||
raise Error, "Timed out waiting for #{pattern.inspect}. Seen:\n#{txt}" | ||
end | ||
|
||
# @param keys [String] "C-X" for Ctrl-X, "M-X" for Alt-X, think "Meta"; | ||
# for details see: | ||
# man tmux | less +/"^KEY BINDINGS" | ||
def send_keys(keys) | ||
system "tmux", "send-keys", "-t", session_name, keys | ||
end | ||
|
||
def has_session? | ||
system "tmux", "has-session", "-t", session_name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Um, because of that |
||
end | ||
|
||
def kill_session | ||
system "tmux", "kill-session", "-t", session_name | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#! /usr/bin/env ruby | ||
|
||
require_relative "../test_helper" | ||
require "yast" | ||
|
||
if Yast.ui_component == "" | ||
Yast.ui_component = ARGV[0] || "ncurses" | ||
end | ||
|
||
module Yast | ||
class TableCellClient < Client | ||
def main | ||
Yast.import "UI" | ||
|
||
# notice that neither the ids nor the values are sorted here | ||
contents = [ | ||
Item(Id("id-zzz-1-bbb"), "name-bbb", "value-bbb"), | ||
Item(Id("id-yyy-2-ccc"), "name-ccc", "value-ccc"), | ||
Item(Id("id-xxx-3-aaa"), "name-aaa", "value-aaa"), | ||
] | ||
keep_sorting = WFM.Args()[0] == "no-sort" | ||
opts = keep_sorting ? Opt(:keepSorting, :notify) : Opt(:notify) | ||
UI.OpenDialog( | ||
VBox( | ||
Label("Table sorting test"), | ||
MinSize( | ||
25, 8, | ||
Table(Id(:table), opts, Header("Name", "Value"), contents) | ||
), | ||
Label("Enter/Double-click any item to uppercase the value"), | ||
HBox( | ||
HSquash(Label("Current Item: ")), | ||
Label(Id(:current_item), Opt(:outputField, :hstretch), "...") | ||
), | ||
PushButton(Id(:cancel), "&Close") | ||
) | ||
) | ||
|
||
if WFM.Args()[0] == "change-current-item" | ||
# test boo#1177145, wrong item is selected | ||
UI.ChangeWidget(Id(:table), :CurrentItem, "id-yyy-2-ccc") | ||
current_item_id = UI.QueryWidget(Id(:table), :CurrentItem) | ||
UI.ChangeWidget(Id(:current_item), :Value, current_item_id.inspect) | ||
end | ||
|
||
while UI.UserInput != :cancel | ||
current_item_id = UI.QueryWidget(Id(:table), :CurrentItem) | ||
UI.ChangeWidget(Id(:current_item), :Value, current_item_id.inspect) | ||
|
||
value = UI.QueryWidget(:table, Cell(current_item_id, 1)) | ||
UI.ChangeWidget(Id(:table), Cell(current_item_id, 1), value.upcase) | ||
end | ||
items = UI.QueryWidget(:table, :Items) | ||
Builtins.y2milestone("Items: %1", items) | ||
|
||
UI.CloseDialog | ||
nil | ||
end | ||
end | ||
end | ||
|
||
Yast::TableCellClient.new.main |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
require_relative "rspec_tmux_tui" | ||
|
||
describe "Table" do | ||
context "when it sorts the items," do | ||
around(:each) do |ex| | ||
yast_ncurses = "#{__dir__}/yast_ncurses" | ||
@base = "table_sort" | ||
@tui = TmuxTui.new_session "#{yast_ncurses} #{__dir__}/#{@base}.rb change-current-item" | ||
ex.run | ||
@tui.kill_session if @tui.has_session? | ||
end | ||
|
||
bug = "1165388" | ||
it "ChangeWidget(_, Cell(row, col)) changes the correct cell, boo##{bug}" do | ||
base = @base + "_cell" | ||
@tui.await(/Table sorting test/) | ||
@tui.capture_pane_to("#{base}-1-initial") | ||
|
||
@tui.send_keys "Home" # go to first table row | ||
@tui.capture_pane_to("#{base}-2-first-row-selected") | ||
|
||
@tui.send_keys "Enter" # activate first table row | ||
@tui.capture_pane_to("#{base}-3-first-row-activated") | ||
|
||
expect(@tui.capture_pane).to match(/name-aaa.VALUE-AAA/) | ||
|
||
@tui.send_keys "M-C" # &Close | ||
end | ||
|
||
bug = "1177145" | ||
it "ChangeWidget(_, :CurrentItem) activates the correct line, boo##{bug}" do | ||
skip "not fixed yet" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe marking as https://relishapp.com/rspec/rspec-core/v/3-8/docs/pending-and-skipped-examples |
||
|
||
base = @base + "_current_item" | ||
@tui.await(/Table sorting test/) | ||
@tui.capture_pane_to("#{base}-1-ccc-selected") | ||
# the UI code performs a | ||
# UI.ChangeWidget(Id(:table), :CurrentItem, "id-yyy-2-ccc") | ||
# then | ||
# UI.QueryWidget(Id(:table), :CurrentItem) | ||
@tui.send_keys "Enter" # activate the current item to produce an event | ||
expect(@tui.capture_pane).to match(/Current Item: "id-yyy-2-ccc"/) | ||
|
||
@tui.send_keys "M-C" # &Close | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#! /usr/bin/env ruby | ||
require_relative "../test_helper" | ||
require "yast" | ||
Yast.ui_component = "ncurses" | ||
load ARGV[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP: It would be nice to have a full bug URL here, just having a bug number might not be enough for people not familiar with libyui or SUSE.