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
New interactive test framework based on pexpect #6825
Conversation
This is quite cool! I love how this uses one file per test. I feel that's the single biggest improvement in littlecheck over our previous system. Switching files got old quick, especially between ".err" and ".out" files. I also like how this uses python, which we also use elsewhere and which I know and like, over tcl which I don't know and only ever used for these tests. The dependency seems acceptable, since it seems to be widely available (also the other name), especially via pip. I would love confirmation that it works everywhere (e.g. the BSDs, OpenIndiana/Illumos, cygwin?), but since it's just a testing dependency and we don't currently have CI on any of these I would be okay without. What I don't really love is the output. There is a lot of information here, and it's quite dense, especially considering there's escaped characters in here. There's a few suggestions I'd make:
Also it's currently a bit weird how you have to read top to bottom and then jump to the top again. I.e. you follow the messages, and then the thing that happens is the TIMEOUT, mentioned before the messages. Maybe add a TIMEOUT message to the "table"? Or maybe not, since that's the important thing that happened? But on the other hand the "unmatched" message is now printed in two locations already.
Supporting py3 only is acceptable. See also #6537. |
build_tools/pexpect_helper.py
Outdated
""" Return a RECV message with the given text. """ | ||
return Message(Message.DIR_RECV, text, when) | ||
|
||
def formatted(self): |
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.
I'm not entirely up on idiomatic python. Should this not be __str__
? Then you could remove the explicit call later.
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.
So I've done some work on this, and I've found it nicer to remove the formatting responsibility from this class, so you can do relative timestamps.
Also it is entirely too ready to call everything a "TIMEOUT" or is that on purpose? When I e.g. change a "send" but not the corresponding "except", should it not complain that it's gotten the wrong thing? |
Okay, if I'm reading this correctly pexpect ignores any non-matching output. I'm not a fan of that. It means you can just add more output and the test won't fail. In certain situations our prompt counter could save us, tho. |
I love this! it really takes away most of the pain from writing these tests for me, because it's much easier to learn and debug with a bit of python experience.
Yep, this makes it easy to ignore extra redraws; for example: control_a = '\x01'
sp.send("XYZ" + control_a + "echo ") Here fish has to redraw the line once for each character of |
That's neat, but what about unintentional output? If you cause another prompt to appear we'll still catch it with the prompt counting, and if you change a line that's bracketed with sp.send("echo mno pqr") to sp.send("echo AAAAAAA; echo mno pqr") or e.g. unintentionally change the output of |
I see, so with |
I really can't take any of the blame, er, kudos for the expect-based framework - that was @lilyball's hard work and @faho has done lots to keep it running. I agree that it is time to move to more modern tooling. pexpect is packaged in just about every platform we care about, although sometimes the versions are quite old. |
So I've looked some more into this, and I have no idea how expect does it. You can technically use So: Yeah, it's not all that important. Let's just use it without. |
Expect also just drops unmatched output until the timeout arrives - that's one of its strengths, arguably. |
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.
This is great! I've tested re-enabling the expect/pexpect tests on all the OBS platforms, and they run and pass just fine with the exception of RHEL 6 & 7 where getting Python 3 installed is not straightforward (CentOS 7 is fine).
The expect-based tests were disabled on OBS because of how flaky they were in aa32fc9, but perhaps we can turn them back on once they are migrated to pexpect. There's probably diminishing returns on running them on every known platform, of course.
@@ -14,6 +14,10 @@ matrix: | |||
- libncurses5-dev | |||
- libpcre2-dev | |||
- python | |||
- python3 | |||
- python3-pip |
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.
Just make this python3-pexpect
, and drop the pip
lines on everything except macOS - the system packages install faster and are fine (or I can do it after merge).
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.
Awesome, I'll do it
Thank you all for awesome review and feedback. @faho's output suggestions are a huge improvement, I will certainly implement those before merging. |
|
c253d11
to
f448a7a
Compare
In preparation for the new pexpect-based tests, modify the travis config file to install pexpect.
This adds a new interactive test framework based on Python's pexpect. This is intended to supplant the TCL expect-based tests. New tests go in `tests/pexpects/`. As a proof-of-concept, the pipeline.expect test and the (gnarly) bind.expect test are ported to the new framework.
Make it easier to use pexpect and to understand its error messages. Switch to a style in tests using bound methods, which makes them less noisy to write.
With the new pexpect based framework, bind and pipeline expect tests can be removed. Amusingly the complete.fish check required the existence of bind.expect. Fix the check at the same time.
Merged as 8a27873 @zanchey on Travis, I tried your suggestion of switching from @faho I think I incorporated your suggestions; of course feel free to modify it further as you please. You have a great eye for this stuff. |
This adds a new interactive test framework, intended to eventually supplant the expect-based interactive tests.
Recall that "interactive tests" simulate a tty. They're for testing key bindings, signal handling, and other UI stuff. Existing fish interactive tests are based on expect. Despite heroics from @zanchey and others, expect is still hard to work with. TCL is an idiosyncratic language. It uses dollar-sign variables, which collides with fish. The diff-based framework makes authoring and debugging tests tedious.
I propose incrementally migrating expect based tests to a framework based on pexpect, which is a tty emulator written in Python. This allows us to write tests in Python instead of TCL, and also provides better authoring and debugging niceties.
Design
pexpect tests will use the same vocabulary as expect-based tests:
send()
orsendline()
to send text to fish.expect_str()
andexpect_re()
to match against fish's output. If no matching output is produced before a timeout, this fails the test.expect_prompt()
optionally matches a RE, then a prompt.See bind.py for an example (compare to bind.expect).
Failures
A test failure looks like this:
this shows the line number
bind.py:188
where the failure occurred.Note the table of "messages" which is text that was successfully sent or matched:
This shows us the direction, timestamp (ms since start), and the location in the test file which initiated the send or receive.
Dependency Changes
Tests will run under Python 3 only. Python 2 support is not worth the hassle. Note this is a test-time only dependency; we'll continue to support python2 for user-facing scripts.
Beyond that the
pexpect
module must be installed:pip3 install pexpect
. Note that pexpect uses the pty module, which is untested except on Linux - this is I think the biggest risk. In practice it appears to work fine on Macs, and course ourexpect
tests have historically been flaky on non Linux/Mac platforms.