http://www.perlmonks.org?node_id=835953

webfiend has asked for the wisdom of the Perl Monks concerning the following question:

I've got a Web site heavily spiked with tutorials and sample code. The tutorial programs are very simple, and the user interaction is not what you'd consider demanding.

Given a simple Parrot PIR program like this:

.sub 'main' :main .local string name .local string greeting .local pmc stdin stdin = getstdin name = stdin.'readline_interactive'("Please enter your name: ") greeting = "Hello, " . name greeting .= "!" say greeting .end

The sample usage is straightforward, but bothersome to double-check every time I update my Parrot install.

$ parrot code/example-01-06.pir
Please enter your name: Brian
Hello, Brian!

Manually verifying behavior after a language update has become tiresome. So I wrote some tests. They use IO::Pty::Easy to handle input and output.

use Modern::Perl; use IO::Pty::Easy; use Test::More tests => 3; my $pty = IO::Pty::Easy->new; ok $pty->spawn("parrot", "code/example-01-06.pir"); my $output = $pty->read(); is $output, "Please enter your name: "; $pty->write("Brian\n"); $output = $pty->read(); is $output, "Hello, Brian!\n"; $pty->close;

There's nothing wrong with this code - at least nothing that is obvious to me. I'm just wondering if there is a test approach or IO library preferred by the monks for testing lots of simple scripts of this nature.

Edit: Added the Parrot script being tested to hopefully improve the coherence of my late night post.

Replies are listed 'Best First'.
Re: Suggestions for testing interactive CLI apps?
by Corion (Patriarch) on Apr 21, 2010 at 07:10 UTC

    I would just pipe the interaction sequence from STDIN into the program, and capture the whole STDOUT in one go, and at the end compare whether the interaction was as expected. Most likely IPC::Run is the nicest thing to supply a child process with input and capture its output. I would try to stay away from true interactivity. Just push in a preset sequence of strings and only when the program has stopped, look at the generated output. alarm should be used as a safeguard so no runaway child can stop your whole test suite.

      IPC::Run looks quite nice and easy. It looks like the timeout from the module handles alarm-style behavior.

      Actually, IPC::Run looked so nice and easy that I tried it out. My attempts to use it pleased me, so I wrote a little wrapper to hide some of the clutter.

      package Test::CLI; use Modern::Perl; use IPC::Run qw(run timeout); use Test::More; use base qw(Exporter); our @EXPORT_OK = qw( run_output_is ); our $TIMEOUT = 10; sub run_output_is { my ($expected, $command, $input, $diagnostic) = @_; my ($out, $err); run $command, \$input, \$out, \$err, timeout($TIMEOUT); is $expected, $out, $diagnostic; } 1;

      That allowed for a nice simple script in my test code.

      use Modern::Perl; use Test::CLI qw(run_output_is); use Test::More tests => 1; my @command = qw(parrot code/example-01-06.pir); my $in =<<INPUT Brian INPUT ; my $expected =<<EXPECTED Please enter your name: Brian Hello, Brian! EXPECTED ; run_output_is $expected, \@command, $in;

      So thanks for the suggestion! That worked quite nicely for my needs.

Re: Suggestions for testing interactive CLI apps?
by chromatic (Archbishop) on Apr 21, 2010 at 08:39 UTC

    I haven't used Test::Expect much, but I'd start with that if I had to do something similar.