App-Cmd

 view release on metacpan or  search on metacpan

lib/App/Cmd.pm  view on Meta::CPAN

  $self->_prepare_command($self->default_command, $opt, @sub_args);
}

sub _bad_command {
  my ($self, $command, $opt, @args) = @_;
  print "Unrecognized command: $command.\n\nUsage:\n" if defined($command);

  # This should be class data so that, in Bizarro World, two App::Cmds will not
  # conflict.
  our $_bad++;
  $self->prepare_command(qw(commands --stderr));
}

END { exit 1 if our $_bad };

#pod =method default_command
#pod
#pod This method returns the name of the command to run if none is given on the
#pod command line.  The default default is "help"
#pod
#pod =cut

lib/App/Cmd/Command/commands.pm  view on Meta::CPAN

#pod application's commands and their abstracts.
#pod
#pod The commands are printed in sorted groups (created by C<sort_commands>); each
#pod group is set off by blank lines.
#pod
#pod =cut

sub execute {
  my ($self, $opt, $args) = @_;

  my $target = $opt->stderr ? *STDERR : *STDOUT;
 
  my @cmd_groups = $self->app->command_groups;
  my @primary_commands = map { @$_ if ref $_ } @cmd_groups;

  if (!@cmd_groups) {
    @primary_commands =
      grep { $_ ne 'version' or $self->app->{show_version} }
      map { ($_->command_names)[0] }
      $self->app->command_plugins;

lib/App/Cmd/Command/commands.pm  view on Meta::CPAN

  my $float = qr/^(?:help|commands)$/;

  my @head = sort grep { $_ =~ $float } @commands;
  my @tail = sort grep { $_ !~ $float } @commands;

  return (\@head, \@tail);
}

sub opt_spec {
  return (
    [ 'stderr' => 'hidden' ],
  );
}

1;

__END__

=pod

=encoding UTF-8

lib/App/Cmd/Tester.pm  view on Meta::CPAN

#pod
#pod   use Test::More tests => 4;
#pod   use App::Cmd::Tester;
#pod
#pod   use YourApp;
#pod
#pod   my $result = test_app(YourApp => [ qw(command --opt value) ]);
#pod
#pod   like($result->stdout, qr/expected output/, 'printed what we expected');
#pod
#pod   is($result->stderr, '', 'nothing sent to sderr');
#pod
#pod   is($result->error, undef, 'threw no exceptions');
#pod
#pod   my $result = test_app(YourApp => [ qw(command --opt value --quiet) ]);
#pod
#pod   is($result->output, '', 'absolutely no output with --quiet');
#pod
#pod =head1 DESCRIPTION
#pod
#pod One of the reasons that user-executed programs are so often poorly tested is

lib/App/Cmd/Tester.pm  view on Meta::CPAN

#pod   my $result = test_app($app_class => \@argv_contents);
#pod
#pod This will locally set C<@ARGV> to simulate command line arguments, and will
#pod then call the C<run> method on the given application class (or application).
#pod Output to the standard output and standard error filehandles  will be captured.
#pod
#pod C<$result> is an App::Cmd::Tester::Result object, which has methods to access
#pod the following data:
#pod
#pod   stdout - the output sent to stdout
#pod   stderr - the output sent to stderr
#pod   output - the combined output of stdout and stderr
#pod   error  - the exception thrown by running the application, or undef
#pod   run_rv - the return value of the run method (generally irrelevant)
#pod   exit_code - the numeric exit code that would've been issued (0 is 'okay')
#pod
#pod The output is captured using L<IO::TieCombine>, which I<can> ensure that the
#pod ordering is preserved in the combined output, but I<can't> capture the output
#pod of external programs.  You can reverse these tradeoffs by using
#pod L<App::Cmd::Tester::CaptureExternal> instead.
#pod
#pod =cut

lib/App/Cmd/Tester.pm  view on Meta::CPAN

  });
}

sub _run_with_capture {
  my ($class, $app, $argv) = @_;

  require IO::TieCombine;
  my $hub = IO::TieCombine->new;

  my $stdout = tie local *STDOUT, $hub, 'stdout';
  my $stderr = tie local *STDERR, $hub, 'stderr';

  my $run_rv;

  my $ok = eval {
    local $TEST_IN_PROGRESS = 1;
    local @ARGV = @$argv;
    $run_rv = $app->run;
    1;
  };

  my $error = $ok ? undef : $@;

  return {
    stdout => $hub->slot_contents('stdout'),
    stderr => $hub->slot_contents('stderr'),
    output => $hub->combined_contents,
    error  => $error,
    run_rv => $run_rv,
  };
}

{
  package App::Cmd::Tester::Result 0.337;

  sub new {
    my ($class, $arg) = @_;
    bless $arg => $class;
  }

  for my $attr (qw(app stdout stderr output error run_rv exit_code)) {
    Sub::Install::install_sub({
      code => sub { $_[0]->{$attr} },
      as   => $attr,
    });
  }
}

{
  package App::Cmd::Tester::Exited 0.337;

lib/App/Cmd/Tester.pm  view on Meta::CPAN


  use Test::More tests => 4;
  use App::Cmd::Tester;

  use YourApp;

  my $result = test_app(YourApp => [ qw(command --opt value) ]);

  like($result->stdout, qr/expected output/, 'printed what we expected');

  is($result->stderr, '', 'nothing sent to sderr');

  is($result->error, undef, 'threw no exceptions');

  my $result = test_app(YourApp => [ qw(command --opt value --quiet) ]);

  is($result->output, '', 'absolutely no output with --quiet');

=head1 DESCRIPTION

One of the reasons that user-executed programs are so often poorly tested is

lib/App/Cmd/Tester.pm  view on Meta::CPAN

  my $result = test_app($app_class => \@argv_contents);

This will locally set C<@ARGV> to simulate command line arguments, and will
then call the C<run> method on the given application class (or application).
Output to the standard output and standard error filehandles  will be captured.

C<$result> is an App::Cmd::Tester::Result object, which has methods to access
the following data:

  stdout - the output sent to stdout
  stderr - the output sent to stderr
  output - the combined output of stdout and stderr
  error  - the exception thrown by running the application, or undef
  run_rv - the return value of the run method (generally irrelevant)
  exit_code - the numeric exit code that would've been issued (0 is 'okay')

The output is captured using L<IO::TieCombine>, which I<can> ensure that the
ordering is preserved in the combined output, but I<can't> capture the output
of external programs.  You can reverse these tradeoffs by using
L<App::Cmd::Tester::CaptureExternal> instead.

=for Pod::Coverage result_class

lib/App/Cmd/Tester/CaptureExternal.pm  view on Meta::CPAN

#pod
#pod   use Test::More tests => 4;
#pod   use App::Cmd::Tester::CaptureExternal;
#pod
#pod   use YourApp;
#pod
#pod   my $result = test_app(YourApp => [ qw(command --opt value) ]);
#pod
#pod   like($result->stdout, qr/expected output/, 'printed what we expected');
#pod
#pod   is($result->stderr, '', 'nothing sent to sderr');
#pod
#pod   ok($result->output, "STDOUT concatenated with STDERR");
#pod
#pod =head1 DESCRIPTION
#pod
#pod L<App::Cmd::Tester> provides a useful scaffold for testing applications, but it
#pod is unable to capture output generated from any external subprograms that are
#pod invoked from the application.
#pod
#pod This subclass uses an alternate mechanism for capturing output

lib/App/Cmd/Tester/CaptureExternal.pm  view on Meta::CPAN

#pod testing if something appeared in either output stream, but you can't rely on
#pod the ordering being correct between lines to STDOUT and lines to STDERR.
#pod
#pod =cut

sub _run_with_capture {
  my ($class, $app, $argv) = @_;

  my $run_rv;

  my ($stdout, $stderr, $ok) = capture {
    eval {
      local $App::Cmd::Tester::TEST_IN_PROGRESS = 1;
      local @ARGV = @$argv;
      $run_rv = $app->run;
      1;
    };
  };

  my $error = $ok ? undef : $@;

  return {
    stdout => $stdout,
    stderr => $stderr,
    output => $stdout . $stderr,
    error  => $error,
    run_rv => $run_rv,
  };
}

1;

__END__

=pod

lib/App/Cmd/Tester/CaptureExternal.pm  view on Meta::CPAN


  use Test::More tests => 4;
  use App::Cmd::Tester::CaptureExternal;

  use YourApp;

  my $result = test_app(YourApp => [ qw(command --opt value) ]);

  like($result->stdout, qr/expected output/, 'printed what we expected');

  is($result->stderr, '', 'nothing sent to sderr');

  ok($result->output, "STDOUT concatenated with STDERR");

=head1 DESCRIPTION

L<App::Cmd::Tester> provides a useful scaffold for testing applications, but it
is unable to capture output generated from any external subprograms that are
invoked from the application.

This subclass uses an alternate mechanism for capturing output

t/setup.t  view on Meta::CPAN

#!perl
use strict;
use warnings;

use Test::More 'no_plan';
use Capture::Tiny 'capture_stderr';

use lib 't/lib';

my $CLASS = 'Test::WithSetup';

require_ok($CLASS);

ok($CLASS->isa('App::Cmd'), "$CLASS subclasses App::Cmd");

my $app = $CLASS->new;

t/setup.t  view on Meta::CPAN

  is_deeply(
    $return,
    {},
    "basically run",
  );
}

{
  local @ARGV = qw(alfie --why);

  my ($stderr, $return) = capture_stderr(sub {
    eval { $app->run }
  });

  is_deeply(
    $return,
    undef,
    "unknown option with overridden getopt_conf caused program to exit",
  );

  like(
    $stderr,
    qr{Unknown option: why},
    "and gives the standard G::L::D missing option message",
  );
}

{
  local @ARGV = qw(bertie);
  my $return = eval { $app->run };

  is($return->[0], 'Test::XyzzyPlugin', "arg0 = plugin itself");

t/tester-exit.t  view on Meta::CPAN


use Capture::Tiny 'capture';
use File::Spec;

require App::Cmd::Tester; # not used, but check which!

my $helper_fn = $0;
$helper_fn =~ s{\.t$}{.helper.pl} or die "Can't make helper from $0";

for my $exit_with (0, 5) {
  my ($stdout, $stderr, $got_exit) = capture {
    system(
      $^X,
      (-d 'blib' ? '-Mblib' : ('-I', File::Spec->rel2abs('lib'))),
      $helper_fn, $exit_with);
  };

  chomp $stdout;
  is($stdout, $INC{'App/Cmd/Tester.pm'}, "App::Cmd::Tester source path")
    unless $exit_with; # just once



( run in 1.315 second using v1.01-cache-2.11-cpan-49f99fa48dc )