Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Outputting input prompt with prove

by nysus (Parson)
on Aug 05, 2019 at 01:28 UTC ( [id://11103899]=perlquestion: print w/replies, xml ) Need Help??

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

I have this test program:

#! /usr/bin/env perl $| = 1; print "test"; print "\n"; print 'Enter: '; <STDIN>
It outputs this when run with prove -lvm:
test.pl .. test

Notice that the Enter: prompt doesn't appear and is getting buffered. I tried various tricks with IO::Handle and other suggestions I googled but none of them worked. I'm on a Mac, Perl ver. 5.24.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Outputting input prompt with prove
by haukex (Archbishop) on Aug 05, 2019 at 09:00 UTC

    You're right that prove doesn't handle that situation very well (I tried ExtUtils::MakeMaker's prompt but that had the same issue), but then again, it's not really meant to, since tests should generally be able to run non-interactively. It sounds more like an XY Problem to me, could you explain what you need this for?

      It's really just something that has annoyed me for a while and I'd like to see if there is a way to print the prompt.

      The current project I'm working on shows sample data from a spreadsheet column and asks the user which col headers to user. It's not really a big deal.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        The current project I'm working on shows sample data from a spreadsheet column and asks the user which col headers to user.

        Well, that wouldn't really make sense in an automated test, so I guess it's only for those people who run the tests manually in their console? In that case, you could provide some other way to pass that parameter in - just one example of many being an environment variable, and in your test script, only run that code when that environment variable is set. Another thing I like to do in my test scripts is use Test::More's diag and note appropriately, so that the output is not cluttered when running prove without -v.

Re: Outputting input prompt with prove
by Tanktalus (Canon) on Aug 05, 2019 at 03:44 UTC

    That's because it's not your process that is buffering it. It's prove. I suspect it is reading line-by-line to be able to intersperse its own output without having stuff appear in funny places. But that means that because your "Enter" line doesn't have a line ending, prove thinks there is still more coming, and won't know you're done until it hits the \n, as there may be an ok(...) in there somewhere, and it needs to print that out properly should it happen.

    That being said, I'm not sure why anything you expect to run under prove would be printing out partial lines or reading from STDIN. Both of those seem to be odd things to put in tests.

      I’m not sure how I could avoid getting input from the user. Is there a way to inject input automatically?

        Did you write the test program? Then rewrite it so it doesn't need interactive input.

        If you didn't write the test program and cannot change it, don't use prove with it or learn that you need to type the input blindly.

        Maybe you can write a wrapper for test that writes the input you want to it and passes its output through, maybe using IPC::Run3.

Re: Outputting input prompt with prove
by davido (Cardinal) on Aug 05, 2019 at 15:09 UTC

    This is a tricky problem, but it has various solutions.

    One solution is to use IPC::Open3, and to test your script by writing to STDIN programatically, and reading from STDOUT. It would be a lot more convenient to just use a module like Capture::Tiny, but that module doesn't really help you solve the writing to STDIN problem very well. IPC::Run may be easier to use for this.

    Another solution is to convert your prompting and input code to use ExtUtils::MakeMaker's prompt function. This function allows you to provide a default value, but also allows you to set the environment variable PERL_MM_USE_DEFAULT to tell prompt to use that default value in lieu of reading from STDIN. It's sort of an odd state of evolution that ExtUtils::MakeMaker contains a prompt function, but ExtUtils::MakeMaker is a core Perl module, as baked into the Perl distribution as strict.

    Though it introduces a dependency to do so, IO::Prompt::Tiny puts the function into a module that is less of a kitchen sink, and more appropriately named for what it provides. It still offers compatibility with the PERL_MM_USE_DEFAULT environment variable. Here is an example script using it:

    #!/usr/bin/env perl use strict; use warnings; use IO::Prompt::Tiny qw(prompt); my $input = prompt('Enter: ', 50); print "$input\n";

    And here is a sample test:

    use Test::More; use Capture::Tiny qw(capture); my ($stdout, $stderr, $exit) = capture { local $ENV{'PERL_MM_USE_DEFAULT'} = 1; system('/usr/bin/env', 'perl', './mytest.pl'); }; ok 0 == $exit, 'Clean exit code.'; like $stderr, qr/^\QIs interactive? No/, 'Detected non-interactive mod +e.'; is $stdout, "Enter: [50] 50\n50\n", 'Correct output.';

    But this still exposes a weakness in going about testing by looking at STDOUT; you're testing at the highest level, and are unable to examine what's going on at lower levels. It's difficult to unit test. If you went the route of using a modulino you could reconfigure your script for better testability. Modulinos are described in the OReilly book, "Mastering Perl", and also in various online resources, one of the earliest of which is http://www.drdobbs.com/scripts-as-modules/184416165.

    By converting to a modulino, breaking functionality into sensible subroutines, and then creating appropriate wrappers around the IO boundaries you can develop tests that validate both the top level code through the use of IO capturing techniques demonstrated above, and also at the unit level. For the unit level tests many of them could mock an output subroutine to avoid hitting STDOUT at all, similar to how prompt allows for hinting STDIN.


    Dave

Re: Outputting input prompt with prove
by 1nickt (Canon) on Aug 05, 2019 at 11:51 UTC

    If you need a value for the test to run, obtain it with ExtUtils::MakeMaker::prompt when building the Makefile. If you are trying to test a program that itself has interactive prompting and you cannot or do not choose to mock it, try Test::Expect.

    Hope this helps!


    The way forward always starts with a minimal test.

      That looks great. Thanks. I had seen the Expect module but wasn't sure how use it with prove.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11103899]
Approved by LanX
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (6)
As of 2024-04-18 21:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found