mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-01 18:25:59 +00:00

- Removed runtime.txt after discussion with Anastasia Alexandrova - Added "use VersionParser" into tests in t/lib when needed - Removed word master from tests for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary - Removed word slave from tests for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary - Updated modules for pt-archiver, pt-config-diff, pt-deadlock-logger, pt-duplicate-key-checker, pt-find, pt-fk-error-logger, pt-heartbeat, pt-index-usage, pt-ioprofile, pt-kill, pt-mysql-summary - Changed mysql_ssl patch, so it is now short option s - Added a check for existing zombies in t/pt-kill/execute_command.t - Added bin/pt-galera-log-explainer to .gitignore
2303 lines
60 KiB
Perl
2303 lines
60 KiB
Perl
#!/usr/bin/perl
|
|
|
|
BEGIN {
|
|
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
|
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
|
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
|
};
|
|
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
use English qw(-no_match_vars);
|
|
use Test::More;
|
|
|
|
use OptionParser;
|
|
use DSNParser;
|
|
use PerconaTest;
|
|
|
|
use Test::More tests => 162;
|
|
my $o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
|
|
isa_ok($o, 'OptionParser');
|
|
|
|
my @opt_specs;
|
|
my %opts;
|
|
my $output;
|
|
|
|
# Prevent print_usage() from breaking lines in the first paragraph
|
|
# at 80 chars width. This paragraph contains $PROGRAM_NAME and that
|
|
# becomes too long when this test is ran from trunk, causing a break.
|
|
# To make this test path-independent, we don't break lines. This only
|
|
# affects testing.
|
|
$ENV{DONT_BREAK_LINES} = 1;
|
|
|
|
# Some tests need a DSNParser but we don't provide a POD with a
|
|
# DSN OPTIONS section that would cause the OptionParser to create
|
|
# a DSNParser automatically. So we create the DSNParser manually
|
|
# and hack it into the OptionParser.
|
|
my $dsn_opts = [
|
|
{
|
|
key => 'A',
|
|
desc => 'Default character set',
|
|
dsn => 'charset',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'D',
|
|
desc => 'Database to use',
|
|
dsn => 'database',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'F',
|
|
desc => 'Only read default options from the given file',
|
|
dsn => 'mysql_read_default_file',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'h',
|
|
desc => 'Connect to host',
|
|
dsn => 'host',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'p',
|
|
desc => 'Password to use when connecting',
|
|
dsn => 'password',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'P',
|
|
desc => 'Port number to use for connection',
|
|
dsn => 'port',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'S',
|
|
desc => 'Socket file to use for connection',
|
|
dsn => 'mysql_socket',
|
|
copy => 1,
|
|
},
|
|
{
|
|
key => 'u',
|
|
desc => 'User for login if not current user',
|
|
dsn => 'user',
|
|
copy => 1,
|
|
},
|
|
];
|
|
my $dp = new DSNParser(opts => $dsn_opts);
|
|
|
|
# #############################################################################
|
|
# Test basic usage.
|
|
# #############################################################################
|
|
|
|
# Quick test of standard interface.
|
|
$o->get_specs("$trunk/t/lib/samples/pod/pod_sample_01.txt");
|
|
%opts = $o->opts();
|
|
ok(
|
|
exists $opts{help},
|
|
'get_specs() basic interface'
|
|
);
|
|
|
|
# More exhaustive test of how the standard interface works internally.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
ok(!$o->has('time'), 'There is no --time yet');
|
|
@opt_specs = $o->_pod_to_specs("$trunk/t/lib/samples/pod/pod_sample_01.txt");
|
|
|
|
# hacky workaround for the next test:
|
|
# since now opt_specs includes the attributes hash, but it's a pain to
|
|
# check for all of those, we simply remove it.
|
|
@opt_specs = map {delete $_->{'attributes'}; $_ } @opt_specs;
|
|
|
|
is_deeply(
|
|
\@opt_specs,
|
|
[
|
|
{ spec=>'database|D=s', group=>'default', desc=>'database string', },
|
|
{ spec=>'port|p=i', group=>'default', desc=>'port (default 3306)', },
|
|
{ spec=>'price=f', group=>'default', desc=>'price float (default 1.23)' },
|
|
{ spec=>'hash-req=H', group=>'default', desc=>'hash that requires a value' },
|
|
{ spec=>'hash-opt=h', group=>'default', desc=>'hash with an optional value'},
|
|
{ spec=>'array-req=A',group=>'default', desc=>'array that requires a value'},
|
|
{ spec=>'array-opt=a',group=>'default',desc=>'array with an optional value'},
|
|
{ spec=>'host=d', group=>'default', desc=>'host DSN' },
|
|
{ spec=>'chunk-size=z', group=>'default', desc=>'chunk size' },
|
|
{ spec=>'time=m', group=>'default', desc=>'time' },
|
|
{ spec=>'help+', group=>'default', desc=>'help cumulative' },
|
|
{ spec=>'other!', group=>'default', desc=>'other negatable' },
|
|
],
|
|
'Convert POD OPTIONS to opt specs (pod_sample_01.txt)',
|
|
);
|
|
|
|
$o->_parse_specs(@opt_specs);
|
|
ok($o->has('time'), 'There is a --time now');
|
|
%opts = $o->opts();
|
|
|
|
is_deeply(
|
|
\%opts,
|
|
{
|
|
'database' => {
|
|
spec => 'database|D=s',
|
|
desc => 'database string',
|
|
group => 'default',
|
|
long => 'database',
|
|
short => 'D',
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 's',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'port' => {
|
|
spec => 'port|p=i',
|
|
desc => 'port (default 3306)',
|
|
group => 'default',
|
|
long => 'port',
|
|
short => 'p',
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'i',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'price' => {
|
|
spec => 'price=f',
|
|
desc => 'price float (default 1.23)',
|
|
group => 'default',
|
|
long => 'price',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'f',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'hash-req' => {
|
|
spec => 'hash-req=s',
|
|
desc => 'hash that requires a value',
|
|
group => 'default',
|
|
long => 'hash-req',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'H',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'hash-opt' => {
|
|
spec => 'hash-opt=s',
|
|
desc => 'hash with an optional value',
|
|
group => 'default',
|
|
long => 'hash-opt',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'h',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'array-req' => {
|
|
spec => 'array-req=s',
|
|
desc => 'array that requires a value',
|
|
group => 'default',
|
|
long => 'array-req',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'A',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'array-opt' => {
|
|
spec => 'array-opt=s',
|
|
desc => 'array with an optional value',
|
|
group => 'default',
|
|
long => 'array-opt',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'a',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'host' => {
|
|
spec => 'host=s',
|
|
desc => 'host DSN',
|
|
group => 'default',
|
|
long => 'host',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'd',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'chunk-size' => {
|
|
spec => 'chunk-size=s',
|
|
desc => 'chunk size',
|
|
group => 'default',
|
|
long => 'chunk-size',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'z',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'time' => {
|
|
spec => 'time=s',
|
|
desc => 'time',
|
|
group => 'default',
|
|
long => 'time',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 'm',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'help' => {
|
|
spec => 'help+',
|
|
desc => 'help cumulative',
|
|
group => 'default',
|
|
long => 'help',
|
|
short => undef,
|
|
is_cumulative => 1,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => undef,
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'other' => {
|
|
spec => 'other!',
|
|
desc => 'other negatable',
|
|
group => 'default',
|
|
long => 'other',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 1,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => undef,
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
}
|
|
},
|
|
'Parse opt specs'
|
|
);
|
|
|
|
%opts = $o->short_opts();
|
|
is_deeply(
|
|
\%opts,
|
|
{
|
|
'D' => 'database',
|
|
'p' => 'port',
|
|
},
|
|
'Short opts => log opts'
|
|
);
|
|
|
|
# get() single option
|
|
is(
|
|
$o->get('database'),
|
|
undef,
|
|
'Get valueless long opt'
|
|
);
|
|
is(
|
|
$o->get('p'),
|
|
undef,
|
|
'Get valuless short opt'
|
|
);
|
|
eval { $o->get('foo'); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option foo does not exist/,
|
|
'Die trying to get() nonexistent long opt'
|
|
);
|
|
eval { $o->get('x'); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option x does not exist/,
|
|
'Die trying to get() nonexistent short opt'
|
|
);
|
|
|
|
# set()
|
|
$o->set('database', 'foodb');
|
|
is(
|
|
$o->get('database'),
|
|
'foodb',
|
|
'Set long opt'
|
|
);
|
|
$o->set('p', 12345);
|
|
is(
|
|
$o->get('p'),
|
|
12345,
|
|
'Set short opt'
|
|
);
|
|
eval { $o->set('foo', 123); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option foo does not exist/,
|
|
'Die trying to set() nonexistent long opt'
|
|
);
|
|
eval { $o->set('x', 123); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option x does not exist/,
|
|
'Die trying to set() nonexistent short opt'
|
|
);
|
|
|
|
# got()
|
|
@ARGV = qw(--port 12345);
|
|
$o->get_opts();
|
|
is(
|
|
$o->got('port'),
|
|
1,
|
|
'Got long opt'
|
|
);
|
|
is(
|
|
$o->got('p'),
|
|
1,
|
|
'Got short opt'
|
|
);
|
|
is(
|
|
$o->got('database'),
|
|
0,
|
|
'Did not "got" long opt'
|
|
);
|
|
is(
|
|
$o->got('D'),
|
|
0,
|
|
'Did not "got" short opt'
|
|
);
|
|
|
|
eval { $o->got('foo'); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option foo does not exist/,
|
|
'Die trying to got() nonexistent long opt',
|
|
);
|
|
eval { $o->got('x'); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option x does not exist/,
|
|
'Die trying to got() nonexistent short opt',
|
|
);
|
|
|
|
@ARGV = qw(--bar);
|
|
eval {
|
|
local *STDERR;
|
|
open STDERR, '>', '/dev/null';
|
|
$o->get_opts();
|
|
$o->get('bar');
|
|
};
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option bar does not exist/,
|
|
'Ignore nonexistent opt given on cmd line'
|
|
);
|
|
|
|
@ARGV = qw(--port 12345);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
[],
|
|
'get_opts() resets errors'
|
|
);
|
|
|
|
ok(
|
|
$o->has('D'),
|
|
'Has short opt'
|
|
);
|
|
ok(
|
|
!$o->has('X'),
|
|
'Does not "has" nonexistent short opt'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test hostile, broken usage.
|
|
# #############################################################################
|
|
eval { $o->_pod_to_specs("$trunk/t/lib/samples/pod/pod_sample_02.txt"); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/POD has no OPTIONS section/,
|
|
'Dies on POD without an OPTIONS section'
|
|
);
|
|
|
|
eval { $o->_pod_to_specs("$trunk/t/lib/samples/pod/pod_sample_03.txt"); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/No valid specs in OPTIONS/,
|
|
'Dies on POD with an OPTIONS section but no option items'
|
|
);
|
|
|
|
eval { $o->_pod_to_specs("$trunk/t/lib/samples/pod/pod_sample_04.txt"); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/No description after option spec foo/,
|
|
'Dies on option with no description'
|
|
);
|
|
|
|
# TODO: more hostile tests: duplicate opts, can't parse long opt from spec,
|
|
# unrecognized rules, ...
|
|
|
|
# #############################################################################
|
|
# Test option defaults.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME [OPTIONS]",
|
|
);
|
|
# These are dog opt specs. They're used by other tests below.
|
|
$o->_parse_specs(
|
|
{
|
|
spec => 'defaultset!',
|
|
desc => 'alignment test with a very long thing '
|
|
. 'that is longer than 80 characters wide '
|
|
. 'and must be wrapped'
|
|
},
|
|
{ spec => 'defaults-file|F=s', desc => 'alignment test' },
|
|
{ spec => 'dog|D=s', desc => 'Dogs are fun' },
|
|
{ spec => 'foo!', desc => 'Foo' },
|
|
{ spec => 'love|l+', desc => 'And peace' },
|
|
);
|
|
|
|
is_deeply(
|
|
$o->get_defaults(),
|
|
{},
|
|
'No default defaults',
|
|
);
|
|
|
|
$o->set_defaults(foo => 1);
|
|
is_deeply(
|
|
$o->get_defaults(),
|
|
{
|
|
foo => 1,
|
|
},
|
|
'set_defaults() with values'
|
|
);
|
|
|
|
$o->set_defaults();
|
|
is_deeply(
|
|
$o->get_defaults(),
|
|
{},
|
|
'set_defaults() without values unsets defaults'
|
|
);
|
|
|
|
# We've already tested opt spec parsing,
|
|
# but we do it again for thoroughness.
|
|
%opts = $o->opts();
|
|
is_deeply(
|
|
\%opts,
|
|
{
|
|
'foo' => {
|
|
spec => 'foo!',
|
|
desc => 'Foo',
|
|
group => 'default',
|
|
long => 'foo',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 1,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => undef,
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'defaultset' => {
|
|
spec => 'defaultset!',
|
|
desc => 'alignment test with a very long thing '
|
|
. 'that is longer than 80 characters wide '
|
|
. 'and must be wrapped',
|
|
group => 'default',
|
|
long => 'defaultset',
|
|
short => undef,
|
|
is_cumulative => 0,
|
|
is_negatable => 1,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => undef,
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'defaults-file' => {
|
|
spec => 'defaults-file|F=s',
|
|
desc => 'alignment test',
|
|
group => 'default',
|
|
long => 'defaults-file',
|
|
short => 'F',
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 's',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'dog' => {
|
|
spec => 'dog|D=s',
|
|
desc => 'Dogs are fun',
|
|
group => 'default',
|
|
long => 'dog',
|
|
short => 'D',
|
|
is_cumulative => 0,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => 's',
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
'love' => {
|
|
spec => 'love|l+',
|
|
desc => 'And peace',
|
|
group => 'default',
|
|
long => 'love',
|
|
short => 'l',
|
|
is_cumulative => 1,
|
|
is_negatable => 0,
|
|
is_required => 0,
|
|
is_repeatable => 0,
|
|
type => undef,
|
|
got => 0,
|
|
value => undef,
|
|
attributes => {},
|
|
},
|
|
},
|
|
'Parse dog specs'
|
|
);
|
|
|
|
$o->set_defaults('dog' => 'fido');
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('dog'),
|
|
'fido',
|
|
'Opt gets default value'
|
|
);
|
|
is(
|
|
$o->got('dog'),
|
|
0,
|
|
'Did not "got" opt with default value'
|
|
);
|
|
|
|
@ARGV = qw(--dog rover);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('dog'),
|
|
'rover',
|
|
'Value given on cmd line overrides default value'
|
|
);
|
|
|
|
eval { $o->set_defaults('bone' => 1) };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Cannot set default for nonexistent option bone/,
|
|
'Cannot set default for nonexistent option'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test option attributes negatable and cumulative.
|
|
# #############################################################################
|
|
|
|
# These tests use the dog opt specs from above.
|
|
|
|
@ARGV = qw(--nofoo);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('foo'),
|
|
0,
|
|
'Can negate negatable opt like --nofoo'
|
|
);
|
|
|
|
@ARGV = qw(--no-foo);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('foo'),
|
|
0,
|
|
'Can negate negatable opt like --no-foo'
|
|
);
|
|
|
|
@ARGV = qw(--nodog);
|
|
{
|
|
local *STDERR;
|
|
open STDERR, '>', '/dev/null';
|
|
$o->get_opts();
|
|
}
|
|
is_deeply(
|
|
$o->get('dog'),
|
|
undef,
|
|
'Cannot negate non-negatable opt'
|
|
);
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Error parsing options'],
|
|
'Trying to negate non-negatable opt sets an error'
|
|
);
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('love'),
|
|
0,
|
|
'Cumulative defaults to 0 when not given'
|
|
);
|
|
|
|
@ARGV = qw(--love -l -l);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('love'),
|
|
3,
|
|
'Cumulative opt val increases (--love -l -l)'
|
|
);
|
|
is(
|
|
$o->got('love'),
|
|
1,
|
|
"got('love') when given multiple times short and long"
|
|
);
|
|
|
|
@ARGV = qw(--love);
|
|
$o->get_opts();
|
|
is(
|
|
$o->got('love'),
|
|
1,
|
|
"got('love') long once"
|
|
);
|
|
|
|
@ARGV = qw(-l);
|
|
$o->get_opts();
|
|
is(
|
|
$o->got('l'),
|
|
1,
|
|
"got('l') short once"
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test usage output.
|
|
# #############################################################################
|
|
|
|
# The following one test uses the dog opt specs from above.
|
|
|
|
my $prog_name = $PROGRAM_NAME;
|
|
$prog_name =~ s/([\.\/\\])/\\$1/g;
|
|
|
|
my $home_esc = $ENV{HOME};
|
|
$home_esc =~ s/([\.\/\\])/\\$1/g;
|
|
|
|
my $trunk_esc = $trunk;
|
|
$trunk_esc =~ s/([\.\/\\])/\\$1/g;
|
|
|
|
sub check_usage {
|
|
my ( $o, $file ) = @_;
|
|
die "I need an OptionParser object" unless $o;
|
|
die "I need a file name" unless $file;
|
|
|
|
no_diff(
|
|
$o->print_usage(),
|
|
"t/lib/samples/OptionParser/$file",
|
|
cmd_output => 1,
|
|
sed => [
|
|
"'s/ $prog_name/ \$PROGRAM_NAME/g'",
|
|
"'s/$trunk_esc/\$trunk/g'",
|
|
"'s/$home_esc/\$ENV\{HOME\}/g'",
|
|
],
|
|
),
|
|
}
|
|
|
|
# Clear values from previous tests.
|
|
$o->set_defaults();
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
|
|
ok(
|
|
check_usage($o, "help001.txt"),
|
|
'Options aligned and custom prompt included'
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'database|D=s', desc => 'Specify the database for all tables' },
|
|
{ spec => 'nouniquechecks!', desc => 'Set UNIQUE_CHECKS=0 before LOAD DATA INFILE' },
|
|
);
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help002.txt"),
|
|
'Really long option aligns with shorts, and prompt defaults to <options>'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test _get_participants()
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo', desc => 'opt' },
|
|
{ spec => 'bar-bar!', desc => 'opt' },
|
|
{ spec => 'baz', desc => 'opt' },
|
|
);
|
|
is_deeply(
|
|
[ $o->_get_participants('L<"--foo"> disables --bar-bar and C<--baz>') ],
|
|
[qw(foo bar-bar baz)],
|
|
'Extract option names from a string',
|
|
);
|
|
|
|
is_deeply(
|
|
[ $o->_get_participants('L<"--foo"> disables L<"--[no]bar-bar">.') ],
|
|
[qw(foo bar-bar)],
|
|
'Extract [no]-negatable option names from a string',
|
|
);
|
|
# TODO: test w/ opts that don't exist, or short opts
|
|
|
|
# #############################################################################
|
|
# Test required options.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'cat|C=s', desc => 'How to catch the cat; required' }
|
|
);
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Required option --cat must be specified'],
|
|
'Missing required option sets an error',
|
|
);
|
|
|
|
is(
|
|
$o->print_errors(),
|
|
"Usage: $PROGRAM_NAME <options>
|
|
|
|
Errors in command-line arguments:
|
|
* Required option --cat must be specified
|
|
|
|
OptionParser.t parses command line options. For more details, please use the --help option, or try 'perldoc $PROGRAM_NAME' for complete documentation.",
|
|
'Error output includes note about missing required option'
|
|
);
|
|
|
|
@ARGV = qw(--cat net);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('cat'),
|
|
'net',
|
|
'Required option OK',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test option rules.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'ignore|i', desc => 'Use IGNORE for INSERT statements' },
|
|
{ spec => 'replace|r', desc => 'Use REPLACE instead of INSERT statements' },
|
|
'--ignore and --replace are mutually exclusive.',
|
|
);
|
|
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help003.txt"),
|
|
'Usage with rules'
|
|
);
|
|
|
|
@ARGV = qw(--replace);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
[],
|
|
'--replace does not trigger an error',
|
|
);
|
|
|
|
@ARGV = qw(--ignore --replace);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['--ignore and --replace are mutually exclusive.'],
|
|
'Error set when rule violated',
|
|
);
|
|
|
|
# These are used several times in the follow tests.
|
|
my @ird_specs = (
|
|
{ spec => 'ignore|i', desc => 'Use IGNORE for INSERT statements' },
|
|
{ spec => 'replace|r', desc => 'Use REPLACE instead of INSERT statements' },
|
|
{ spec => 'delete|d', desc => 'Delete' },
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'--ignore, --replace and --delete are mutually exclusive.',
|
|
);
|
|
@ARGV = qw(--ignore --replace);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['--ignore, --replace and --delete are mutually exclusive.'],
|
|
'Error set with long opt name and nice commas when rule violated',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
eval {
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'Use one and only one of --insert, --replace, or --delete.',
|
|
);
|
|
};
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option --insert does not exist/,
|
|
'Die on using nonexistent option in one-and-only-one rule'
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'Use one and only one of --ignore, --replace, or --delete.',
|
|
);
|
|
@ARGV = qw(--ignore --replace);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['--ignore, --replace and --delete are mutually exclusive.'],
|
|
'Error set with one-and-only-one rule violated',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'Use one and only one of --ignore, --replace, or --delete.',
|
|
);
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Specify at least one of --ignore, --replace or --delete'],
|
|
'Error set with one-and-only-one when none specified',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'Use at least one of --ignore, --replace, or --delete.',
|
|
);
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Specify at least one of --ignore, --replace or --delete'],
|
|
'Error set with at-least-one when none specified',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
@ird_specs,
|
|
'Use at least one of --ignore, --replace, or --delete.',
|
|
);
|
|
@ARGV = qw(-ir);
|
|
$o->get_opts();
|
|
ok(
|
|
$o->get('ignore') == 1 && $o->get('replace') == 1,
|
|
'Multiple options OK for at-least-one',
|
|
);
|
|
use Data::Dumper;
|
|
$Data::Dumper::Indent=1;
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=i', desc => 'Foo disables --bar' },
|
|
{ spec => 'bar', desc => 'Bar (default 1)' },
|
|
);
|
|
@ARGV = qw(--foo 5);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
[ $o->get('foo'), $o->get('bar') ],
|
|
[ 5, undef ],
|
|
'--foo disables --bar',
|
|
);
|
|
%opts = $o->opts();
|
|
is_deeply(
|
|
$opts{'bar'},
|
|
{
|
|
spec => 'bar',
|
|
is_required => 0,
|
|
value => undef,
|
|
is_cumulative => 0,
|
|
short => undef,
|
|
group => 'default',
|
|
got => 0,
|
|
is_negatable => 0,
|
|
is_repeatable => 0,
|
|
desc => 'Bar (default 1)',
|
|
long => 'bar',
|
|
type => undef,
|
|
parsed => 1,
|
|
attributes => {},
|
|
},
|
|
'Disabled opt is not destroyed'
|
|
);
|
|
|
|
# Option can't disable a nonexistent option.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
eval {
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=i', desc => 'Foo disables --fox' },
|
|
{ spec => 'bar', desc => 'Bar (default 1)' },
|
|
);
|
|
};
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option --fox does not exist/,
|
|
'Invalid option name in disable rule',
|
|
);
|
|
|
|
# Option can't 'allowed with' a nonexistent option.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
eval {
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=i', desc => 'Foo disables --bar' },
|
|
{ spec => 'bar', desc => 'Bar (default 0)' },
|
|
'allowed with --foo: --fox',
|
|
);
|
|
};
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Option --fox does not exist/,
|
|
'Invalid option name in \'allowed with\' rule',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test default values encoded in description.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=i', desc => 'Foo (default 5)' },
|
|
{ spec => 'bar', desc => 'Bar (default)' },
|
|
{ spec => 'price=f', desc => 'Price (default 12345.123456)' },
|
|
{ spec => 'size=z', desc => 'Size (default 128M)' },
|
|
{ spec => 'time=m', desc => 'Time (default 24h)' },
|
|
{ spec => 'host=d', desc => 'Host (default h=127.1,P=12345)' },
|
|
);
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('foo'),
|
|
5,
|
|
'Default integer value encoded in description'
|
|
);
|
|
is(
|
|
$o->get('bar'),
|
|
1,
|
|
'Default option enabled encoded in description'
|
|
);
|
|
is(
|
|
$o->get('price'),
|
|
12345.123456,
|
|
'Default float value encoded in description'
|
|
);
|
|
is(
|
|
$o->get('size'),
|
|
134217728,
|
|
'Default size value encoded in description'
|
|
);
|
|
is(
|
|
$o->get('time'),
|
|
86400,
|
|
'Default time value encoded in description'
|
|
);
|
|
is_deeply(
|
|
$o->get('host'),
|
|
{
|
|
S => undef,
|
|
F => undef,
|
|
A => undef,
|
|
p => undef,
|
|
u => undef,
|
|
h => '127.1',
|
|
D => undef,
|
|
P => '12345'
|
|
},
|
|
'Default host value encoded in description'
|
|
);
|
|
|
|
is(
|
|
$o->got('foo'),
|
|
0,
|
|
'Did not "got" --foo with encoded default'
|
|
);
|
|
is(
|
|
$o->got('bar'),
|
|
0,
|
|
'Did not "got" --bar with encoded default'
|
|
);
|
|
is(
|
|
$o->got('price'),
|
|
0,
|
|
'Did not "got" --price with encoded default'
|
|
);
|
|
is(
|
|
$o->got('size'),
|
|
0,
|
|
'Did not "got" --size with encoded default'
|
|
);
|
|
is(
|
|
$o->got('time'),
|
|
0,
|
|
'Did not "got" --time with encoded default'
|
|
);
|
|
is(
|
|
$o->got('host'),
|
|
0,
|
|
'Did not "got" --host with encoded default'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test size option type.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'size=z', desc => 'size' }
|
|
);
|
|
|
|
@ARGV = qw(--size 5k);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('size'),
|
|
1024*5,
|
|
'5K expanded',
|
|
);
|
|
|
|
@ARGV = qw(--size -5k);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('size'),
|
|
-1024*5,
|
|
'-5K expanded',
|
|
);
|
|
|
|
@ARGV = qw(--size +5k);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('size'),
|
|
'+' . (1024*5),
|
|
'+5K expanded',
|
|
);
|
|
|
|
@ARGV = qw(--size 5);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('size'),
|
|
5,
|
|
'5 expanded',
|
|
);
|
|
|
|
@ARGV = qw(--size 5z);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Invalid size for --size: 5z'],
|
|
'Bad size argument sets an error',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test time option type.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 't=m', desc => 'Time' },
|
|
{ spec => 's=m', desc => 'Time (suffix s)' },
|
|
{ spec => 'm=m', desc => 'Time (suffix m)' },
|
|
{ spec => 'h=m', desc => 'Time (suffix h)' },
|
|
{ spec => 'd=m', desc => 'Time (suffix d)' },
|
|
);
|
|
|
|
@ARGV = qw(-t 10 -s 20 -m 30 -h 40 -d 50);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('t'),
|
|
10,
|
|
'Time value with default suffix decoded',
|
|
);
|
|
is_deeply(
|
|
$o->get('s'),
|
|
20,
|
|
'Time value with s suffix decoded',
|
|
);
|
|
is_deeply(
|
|
$o->get('m'),
|
|
30*60,
|
|
'Time value with m suffix decoded',
|
|
);
|
|
is_deeply(
|
|
$o->get('h'),
|
|
40*3600,
|
|
'Time value with h suffix decoded',
|
|
);
|
|
is_deeply(
|
|
$o->get('d'),
|
|
50*86400,
|
|
'Time value with d suffix decoded',
|
|
);
|
|
|
|
@ARGV = qw(-d 5m);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('d'),
|
|
5*60,
|
|
'Explicit suffix overrides default suffix'
|
|
);
|
|
|
|
# Use shorter, simpler specs to test usage for time blurb.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=m', desc => 'Time' },
|
|
{ spec => 'bar=m', desc => 'Time (suffix m)' },
|
|
);
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help004.txt"),
|
|
'Usage for time value'
|
|
);
|
|
|
|
@ARGV = qw(--foo 5z);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Invalid time suffix for --foo'],
|
|
'Bad time argument sets an error',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test DSN option type.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=d', desc => 'DSN foo' },
|
|
{ spec => 'bar=d', desc => 'DSN bar' },
|
|
'DSN values in --foo default to values in --bar if COPY is yes.',
|
|
);
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help005.txt"),
|
|
'DSN is integrated into help output'
|
|
);
|
|
|
|
@ARGV = ('--bar', 'D=DB,u=USER,h=localhost', '--foo', 'h=otherhost');
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=d', desc => 'DSN foo' },
|
|
{ spec => 'bar=d', desc => 'DSN bar' },
|
|
'DSN values in --foo default to values in --bar if COPY is yes.',
|
|
);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('bar'),
|
|
{
|
|
D => 'DB',
|
|
u => 'USER',
|
|
S => undef,
|
|
F => undef,
|
|
P => undef,
|
|
h => 'localhost',
|
|
p => undef,
|
|
A => undef,
|
|
},
|
|
'DSN parsing on type=d',
|
|
);
|
|
is_deeply(
|
|
$o->get('foo'),
|
|
{
|
|
D => 'DB',
|
|
u => 'USER',
|
|
S => undef,
|
|
F => undef,
|
|
P => undef,
|
|
h => 'otherhost',
|
|
p => undef,
|
|
A => undef,
|
|
},
|
|
'DSN parsing on type=d inheriting from --bar',
|
|
);
|
|
|
|
ok(
|
|
check_usage($o, "help006.txt"),
|
|
'DSN stringified with inheritance into post-processed args'
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
|
|
$o->_parse_specs(
|
|
{ spec => 'foo|f=d', desc => 'DSN foo' },
|
|
{ spec => 'bar|b=d', desc => 'DSN bar' },
|
|
'DSN values in --foo default to values in --bar if COPY is yes.',
|
|
);
|
|
@ARGV = ('-b', 'D=DB,u=USER,h=localhost', '-f', 'h=otherhost');
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('f'),
|
|
{
|
|
D => 'DB',
|
|
u => 'USER',
|
|
S => undef,
|
|
F => undef,
|
|
P => undef,
|
|
h => 'otherhost',
|
|
p => undef,
|
|
A => undef,
|
|
},
|
|
'DSN parsing on type=d inheriting from --bar with short options',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test [Hh]ash and [Aa]rray option types.
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'columns|C=H', desc => 'cols required' },
|
|
{ spec => 'tables|t=h', desc => 'tables optional' },
|
|
{ spec => 'databases|d=A', desc => 'databases required' },
|
|
{ spec => 'books|b=a', desc => 'books optional' },
|
|
{ spec => 'foo=A', desc => 'foo (default a,b,c)' },
|
|
);
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('C'),
|
|
{},
|
|
'required Hash'
|
|
);
|
|
is_deeply(
|
|
$o->get('t'),
|
|
undef,
|
|
'optional hash'
|
|
);
|
|
is_deeply(
|
|
$o->get('d'),
|
|
[],
|
|
'required Array'
|
|
);
|
|
is_deeply(
|
|
$o->get('b'),
|
|
undef,
|
|
'optional array'
|
|
);
|
|
is_deeply($o->get('foo'), [qw(a b c)], 'Array got a default');
|
|
|
|
@ARGV = ('-C', 'a,b', '-t', 'd, e', '-d', 'f,g', '-b', 'o,p' );
|
|
$o->get_opts();
|
|
%opts = (
|
|
C => $o->get('C'),
|
|
t => $o->get('t'),
|
|
d => $o->get('d'),
|
|
b => $o->get('b'),
|
|
);
|
|
is_deeply(
|
|
\%opts,
|
|
{
|
|
C => { a => 1, b => 1 },
|
|
t => { d => 1, e => 1 },
|
|
d => [qw(f g)],
|
|
b => [qw(o p)],
|
|
},
|
|
'Comma-separated lists: all processed when given',
|
|
);
|
|
|
|
ok(
|
|
check_usage($o, "help007.txt"),
|
|
'Lists properly expanded into usage information',
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test groups.
|
|
# #############################################################################
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/t/lib/samples/pod/pod_sample_05.txt");
|
|
|
|
is_deeply(
|
|
$o->get_groups(),
|
|
{
|
|
'Help' => {
|
|
'explain-hosts' => 1,
|
|
'help' => 1,
|
|
'version' => 1,
|
|
},
|
|
'Filter' => { 'databases' => 1, },
|
|
'Output' => { 'tab' => 1, },
|
|
'Connection' => { 'defaults-file' => 1, },
|
|
'default' => {
|
|
'algorithm' => 1,
|
|
'schema' => 1,
|
|
}
|
|
},
|
|
'get_groups()'
|
|
);
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help008.txt"),
|
|
'Option groupings usage',
|
|
);
|
|
|
|
@ARGV = qw(--schema --tab);
|
|
$o->get_opts();
|
|
ok(
|
|
$o->get('schema') && $o->get('tab'),
|
|
'Opt allowed with opt from allowed group'
|
|
);
|
|
|
|
@ARGV = qw(--schema --algorithm ACCUM);
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->errors(),
|
|
['--schema is not allowed with --algorithm'],
|
|
'Opt is not allowed with opt from restricted group'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test clone().
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'user=s', desc => 'User', },
|
|
{ spec => 'dog|d', desc => 'dog option', group => 'Dogs', },
|
|
{ spec => 'cat|c', desc => 'cat option', group => 'Cats', },
|
|
);
|
|
@ARGV = qw(--user foo --dog);
|
|
$o->get_opts();
|
|
|
|
my $o_clone = $o->clone();
|
|
isa_ok(
|
|
$o_clone,
|
|
'OptionParser'
|
|
);
|
|
ok(
|
|
$o_clone->has('user') && $o_clone->has('dog') && $o_clone->has('cat'),
|
|
'Clone has same opts'
|
|
);
|
|
|
|
$o_clone->set('user', 'Bob');
|
|
is(
|
|
$o->get('user'),
|
|
'foo',
|
|
'Change to clone does not change original'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Test issues. Any other tests should find their proper place above.
|
|
# #############################################################################
|
|
|
|
# #############################################################################
|
|
# Issue 140: Check that new style =item --[no]foo works like old style:
|
|
# =item --foo
|
|
# negatable: yes
|
|
# #############################################################################
|
|
@opt_specs = $o->_pod_to_specs("$trunk/t/lib/samples/pod/pod_sample_issue_140.txt");
|
|
is_deeply(
|
|
\@opt_specs,
|
|
[
|
|
{ spec => 'foo', desc => 'Basic foo', group => 'default', attributes => {} },
|
|
{ spec => 'bar!', desc => 'New negatable bar', group => 'default', attributes => { negatable => 1 } },
|
|
],
|
|
'New =item --[no]foo style for negatables'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Issue 92: extract a paragraph from POD.
|
|
# #############################################################################
|
|
is(
|
|
$o->read_para_after("$trunk/t/lib/samples/pod/pod_sample_issue_92.txt", qr/magic/),
|
|
'This is the paragraph, hooray',
|
|
'read_para_after'
|
|
);
|
|
|
|
# The first time I wrote this, I used the /o flag to the regex, which means you
|
|
# always get the same thing on each subsequent call no matter what regex you
|
|
# pass in. This is to test and make sure I don't do that again.
|
|
is(
|
|
$o->read_para_after("$trunk/t/lib/samples/pod/pod_sample_issue_92.txt", qr/abracadabra/),
|
|
'This is the next paragraph, hooray',
|
|
'read_para_after again'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Issue 231: read configuration files
|
|
# #############################################################################
|
|
is_deeply(
|
|
[$o->_read_config_file("$trunk/t/lib/samples/config_file_1.conf")],
|
|
['--foo', 'bar', '--verbose', '/path/to/file', 'h=127.1,P=12346'],
|
|
'Reads a config file',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'config=A', desc => 'Read this comma-separated list of config '
|
|
. 'files (must be the first option on the command line).', },
|
|
{ spec => 'cat=A', desc => 'cat option (default a,b)', },
|
|
);
|
|
|
|
is_deeply(
|
|
[$o->get_defaults_files()],
|
|
["/etc/percona-toolkit/percona-toolkit.conf",
|
|
"/etc/percona-toolkit/OptionParser.t.conf",
|
|
"$ENV{HOME}/.percona-toolkit.conf",
|
|
"$ENV{HOME}/.OptionParser.t.conf"],
|
|
"default options files",
|
|
);
|
|
ok(!$o->got('config'), 'Did not got --config');
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'config=A', desc => 'Read this comma-separated list of config '
|
|
. 'files (must be the first option on the command line).', },
|
|
{ spec => 'cat=A', desc => 'cat option (default a,b)', },
|
|
);
|
|
|
|
$o->get_opts();
|
|
ok(
|
|
check_usage($o, "help009.txt"),
|
|
'Sets special config file default value',
|
|
);
|
|
|
|
@ARGV=qw(--config /path/to/config --cat);
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
|
|
$o->_parse_specs(
|
|
{ spec => 'config=A', desc => 'Read this comma-separated list of config '
|
|
. 'files (must be the first option on the command line).', },
|
|
{ spec => 'cat', desc => 'cat option', },
|
|
);
|
|
eval { $o->get_opts(); };
|
|
like($EVAL_ERROR, qr/Cannot open/, 'No config file found');
|
|
|
|
@ARGV = ('--config',"$trunk/t/lib/samples/empty",'--cat');
|
|
$o->get_opts();
|
|
ok($o->got('config'), 'Got --config');
|
|
|
|
ok(
|
|
check_usage($o, "help010.txt"),
|
|
'Parses special --config option first',
|
|
);
|
|
|
|
@ARGV = ("--config=$trunk/t/lib/samples/empty",'--cat');
|
|
$o->get_opts();
|
|
ok($o->got('config'), 'Got --config=');
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'config=A', desc => 'Read this comma-separated list of config '
|
|
. 'files (must be the first option on the command line).', },
|
|
{ spec => 'cat', desc => 'cat option', },
|
|
);
|
|
|
|
@ARGV=qw(--cat --config /path/to/config);
|
|
{
|
|
local *STDERR;
|
|
open STDERR, '>', '/dev/null';
|
|
$o->get_opts();
|
|
}
|
|
is_deeply(
|
|
$o->errors(),
|
|
['Error parsing options', 'Unrecognized command-line options /path/to/config'],
|
|
'special --config option not given first',
|
|
);
|
|
|
|
# And now we can actually get it to read a config file into the options!
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>",
|
|
);
|
|
is($o->{strict}, 1, "Strict mode enabled by default");
|
|
$o->_parse_specs(
|
|
"This tool accepts additional command-line arguments. Refer to the synopsis and usage information for details.",
|
|
{ spec => 'config=A', desc => 'Read this comma-separated list of config '
|
|
. 'files (must be the first option on the command line).', },
|
|
{ spec => 'foo=s', desc => 'foo option', },
|
|
{ spec => 'verbose+', desc => 'increase verbosity', },
|
|
);
|
|
is($o->{strict}, 0, "Special rule disables strict mode");
|
|
|
|
@ARGV = ('--config', "$trunk/t/lib/samples/config_file_1.conf");
|
|
$o->get_opts();
|
|
is_deeply(
|
|
[@ARGV],
|
|
['/path/to/file', 'h=127.1,P=12346'],
|
|
'Config file influences @ARGV',
|
|
);
|
|
ok($o->got('foo'), 'Got --foo');
|
|
is($o->get('foo'), 'bar', 'Got --foo value');
|
|
ok($o->got('verbose'), 'Got --verbose');
|
|
is($o->get('verbose'), 1, 'Got --verbose value');
|
|
|
|
@ARGV = ('--config', "$trunk/t/lib/samples/config_file_1.conf,$trunk/t/lib/samples/config_file_2.conf");
|
|
$o->get_opts();
|
|
is_deeply(
|
|
[@ARGV],
|
|
['/path/to/file', 'h=127.1,P=12346', '/path/to/file'],
|
|
'Second config file influences @ARGV',
|
|
);
|
|
ok($o->got('foo'), 'Got --foo again');
|
|
is($o->get('foo'), 'baz', 'Got overridden --foo value');
|
|
ok($o->got('verbose'), 'Got --verbose twice');
|
|
is($o->get('verbose'), 2, 'Got --verbose value twice');
|
|
|
|
# #############################################################################
|
|
# Issue 409: OptionParser modifies second value of
|
|
# ' -- .*','(\w+): ([^,]+)' for array type opt
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=a', desc => 'foo' },
|
|
);
|
|
@ARGV = ('--foo', ' -- .*,(\w+): ([^\,]+)');
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('foo'),
|
|
[
|
|
' -- .*',
|
|
'(\w+): ([^\,]+)',
|
|
],
|
|
'Array of vals with internal commas (issue 409)'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Issue 349: Make OptionParser die on unrecognized attributes
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
eval { $o->get_specs("$trunk/t/lib/samples/pod/pod_sample_06.txt"); };
|
|
like(
|
|
$EVAL_ERROR,
|
|
qr/Unrecognized attribute for --verbose: culumative/,
|
|
'Die on unrecognized attribute'
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 460: mk-archiver does not inherit DSN as documented
|
|
# #############################################################################
|
|
|
|
# The problem is actually in how OptionParser handles copying DSN vals.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->_parse_specs(
|
|
{ spec => 'source=d', desc => 'source', },
|
|
{ spec => 'dest=d', desc => 'dest', },
|
|
'DSN values in --dest default to values from --source if COPY is yes.',
|
|
);
|
|
@ARGV = (
|
|
'--source', 'h=127.1,P=12345,D=test,u=bob,p=foo',
|
|
'--dest', 'P=12346',
|
|
);
|
|
$o->get_opts();
|
|
my $dest_dsn = $o->get('dest');
|
|
is_deeply(
|
|
$dest_dsn,
|
|
{
|
|
A => undef,
|
|
D => 'test',
|
|
F =>undef,
|
|
P => '12346',
|
|
S => undef,
|
|
h => '127.1',
|
|
p => 'foo',
|
|
u => 'bob',
|
|
},
|
|
'Copies DSN values correctly (issue 460)'
|
|
);
|
|
|
|
# Test DSN can be repeatable
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->_parse_specs(
|
|
{ spec => 'rep=d', desc => 'source', attributes => { repeatable => 1}, },
|
|
);
|
|
@ARGV = (
|
|
'--rep', 'h=127.1,P=12345,D=test,u=bob,p=foo', '--rep', 'h=127.1,P=12346',
|
|
);
|
|
$o->get_opts();
|
|
my $ddest_dsn = $o->get('rep');
|
|
is_deeply(
|
|
$ddest_dsn,
|
|
[
|
|
{
|
|
A => undef,
|
|
u => 'bob',
|
|
D => 'test',
|
|
h => '127.1',
|
|
P => '12345',
|
|
S => undef,
|
|
p => 'foo',
|
|
F => undef
|
|
},
|
|
{
|
|
p => undef,
|
|
F => undef,
|
|
u => undef,
|
|
D => undef,
|
|
A => undef,
|
|
P => '12346',
|
|
S => undef,
|
|
h => '127.1'
|
|
}
|
|
],
|
|
'DSN type can be repeatable'
|
|
);
|
|
# #############################################################################
|
|
# Issue 248: Add --user, --pass, --host, etc to all tools
|
|
# #############################################################################
|
|
|
|
# See the 5 cases (i.-v.) at http://groups.google.com/group/maatkit-discuss/browse_thread/thread/f4bf1e659c60f03e
|
|
|
|
# case ii.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->get_specs("$trunk/bin/pt-archiver");
|
|
@ARGV = (
|
|
'--source', 'h=127.1,S=/tmp/mysql.socket',
|
|
'--port', '12345',
|
|
'--user', 'bob',
|
|
'--password', 'foo',
|
|
'--socket', '/tmp/bad.socket', # should *not* override DSN
|
|
'--where', '1=1', # required
|
|
);
|
|
$o->get_opts();
|
|
my $src_dsn = $o->get('source');
|
|
is_deeply(
|
|
$src_dsn,
|
|
{
|
|
a => undef,
|
|
A => undef,
|
|
b => undef,
|
|
D => undef,
|
|
F => undef,
|
|
i => undef,
|
|
m => undef,
|
|
P => '12345',
|
|
S => '/tmp/mysql.socket',
|
|
h => '127.1',
|
|
p => 'foo',
|
|
t => undef,
|
|
u => 'bob',
|
|
L => undef,
|
|
s => undef,
|
|
},
|
|
'DSN opt gets missing vals from --host, --port, etc. (issue 248)',
|
|
);
|
|
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->get_specs("$trunk/bin/pt-archiver");
|
|
# Like case ii. but make sure --dest copies u from --source, not --user.
|
|
@ARGV = (
|
|
'--source', 'h=127.1,u=bob',
|
|
'--dest', 'h=127.1',
|
|
'--user', 'wrong_user',
|
|
'--where', '1=1', # required
|
|
);
|
|
$o->get_opts();
|
|
$dest_dsn = $o->get('dest');
|
|
is_deeply(
|
|
$dest_dsn,
|
|
{
|
|
a => undef,
|
|
A => undef,
|
|
b => undef,
|
|
D => undef,
|
|
F => undef,
|
|
i => undef,
|
|
m => undef,
|
|
P => undef,
|
|
S => undef,
|
|
h => '127.1',
|
|
p => undef,
|
|
t => undef,
|
|
u => 'bob',
|
|
L => undef,
|
|
s => undef,
|
|
},
|
|
'Vals from "defaults to" DSN take precedence over defaults (issue 248)'
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 617: Command line options do no override config file options
|
|
# #############################################################################
|
|
diag(`echo "iterations=4" > ~/.OptionParser.t.conf`);
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/bin/pt-query-digest");
|
|
@ARGV = (qw(--iterations 9));
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('iterations'),
|
|
9,
|
|
'Cmd line opt overrides conf (issue 617)'
|
|
);
|
|
diag(`rm -rf ~/.OptionParser.t.conf`);
|
|
|
|
# #############################################################################
|
|
# Issue 623: --since +N does not work in mk-parallel-dump
|
|
# #############################################################################
|
|
|
|
# time type opts need to allow leading +/-
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/bin/pt-query-digest");
|
|
@ARGV = (qw(--run-time +9));
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('run-time'),
|
|
'+9',
|
|
'+N time value'
|
|
);
|
|
|
|
@ARGV = (qw(--run-time -7));
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('run-time'),
|
|
'-7',
|
|
'-N time value'
|
|
);
|
|
|
|
@ARGV = (qw(--run-time +1m));
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('run-time'),
|
|
'+60',
|
|
'+N time value with suffix'
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 829: maatkit: mk-query-digest.1p : provokes warnings from man
|
|
# #############################################################################
|
|
# This happens because --ignore-attributes has a really long default
|
|
# value like val,val,val. man can't break this line unless that list
|
|
# has spaces like val, val, val.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=a', desc => 'foo (default arg, cmd, ip, port)' },
|
|
);
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is_deeply(
|
|
$o->get('foo'),
|
|
[qw(arg cmd ip port)],
|
|
'List vals separated by spaces'
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 940: OptionParser cannot resolve option dependencies
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
# Hack DSNParser into OptionParser. This is just for testing.
|
|
$o->{DSNParser} = $dp;
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=d', desc => 'DSN foo' },
|
|
{ spec => 'bar=d', desc => 'DSN bar' },
|
|
'DSN values in --foo default to values in --bar if COPY is yes.',
|
|
);
|
|
# This simulates what get_opts() does but allows us to call
|
|
# _check_opts() manually with foo first.
|
|
$o->{opts}->{bar} = {
|
|
long => 'bar',
|
|
value => 'D=DB,u=USER,h=localhost',
|
|
got => 1,
|
|
type => 'd',
|
|
};
|
|
$o->{opts}->{foo} = {
|
|
long => 'foo',
|
|
value => 'h=otherhost',
|
|
got => 1,
|
|
type => 'd',
|
|
};
|
|
$o->_check_opts(qw(foo bar));
|
|
is_deeply(
|
|
$o->get('foo'),
|
|
{
|
|
D => 'DB',
|
|
u => 'USER',
|
|
S => undef,
|
|
F => undef,
|
|
P => undef,
|
|
h => 'otherhost',
|
|
p => undef,
|
|
A => undef,
|
|
},
|
|
'Resolves dependency'
|
|
);
|
|
|
|
# Should die on circular dependency, avoid infinite loop.
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=d', desc => 'DSN foo' },
|
|
{ spec => 'bar=d', desc => 'DSN bar' },
|
|
'DSN values in --foo default to values in --bar if COPY is yes.',
|
|
'DSN values in --bar default to values in --foo if COPY is yes.',
|
|
);
|
|
$o->{opts}->{bar} = {
|
|
long => 'bar',
|
|
value => 'D=DB,u=USER,h=localhost',
|
|
got => 1,
|
|
type => 'd',
|
|
};
|
|
$o->{opts}->{foo} = {
|
|
long => 'foo',
|
|
value => 'h=otherhost',
|
|
got => 1,
|
|
type => 'd',
|
|
};
|
|
|
|
throws_ok(
|
|
sub { $o->_check_opts(qw(foo bar)) },
|
|
qr/circular dependencies/,
|
|
'Dies on circular dependency'
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 344
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->_parse_specs(
|
|
{ spec => 'foo=z', desc => 'foo' },
|
|
);
|
|
@ARGV = qw(--foo null);
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('foo'),
|
|
'null',
|
|
'NULL size'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Issue 55: Integrate DSN specifications into POD
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/t/lib/samples/pod/pod_sample_dsn.txt");
|
|
|
|
ok(
|
|
$o->DSNParser(),
|
|
'Auto-created DNSParser obj'
|
|
);
|
|
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
like(
|
|
$o->print_usage(),
|
|
qr/z\s+no\s+/,
|
|
'copy: no'
|
|
);
|
|
|
|
# #############################################################################
|
|
# Issue 1123: OptionParser type: string; default: 0 doesn't work
|
|
# #############################################################################
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/bin/pt-fifo-split");
|
|
@ARGV = ();
|
|
$o->get_opts();
|
|
is(
|
|
$o->get('offset'),
|
|
0,
|
|
"type: string; default: 0 (issue 1123)"
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 338: Make all of tool's --help output come from POD
|
|
# #############################################################################
|
|
$o = new OptionParser(file => "$trunk/bin/pt-archiver");
|
|
|
|
my %synop = $o->_parse_synopsis();
|
|
|
|
is_deeply(
|
|
\%synop,
|
|
{
|
|
usage => "pt-archiver [OPTIONS] --source DSN --where WHERE",
|
|
description => "pt-archiver nibbles records from a MySQL table. The --source and --dest arguments use DSN syntax; if COPY is yes, --dest defaults to the key's value from --source.",
|
|
},
|
|
"_parse_synopsis() gets usage and description"
|
|
);
|
|
|
|
@ARGV = qw(--help);
|
|
$o->get_specs();
|
|
$o->get_opts();
|
|
$output = output(
|
|
sub { $o->usage_or_errors(undef, 1); },
|
|
);
|
|
$synop{usage} =~ s/([\[\]])/\\$1/g;
|
|
like(
|
|
$output,
|
|
qr/^$synop{description} For more details.+\nUsage: $synop{usage}$/m,
|
|
"Uses desc and usage from SYNOPSIS for help"
|
|
);
|
|
|
|
# #############################################################################
|
|
# Bug 1039074: Tools exit 0 on error parsing options, should exit non-zero
|
|
# #############################################################################
|
|
|
|
# pt-archiver requires at least one of --dest, --file or --purge, as well as
|
|
# --where and --source. So specifying no options should cause errors.
|
|
@ARGV = qw();
|
|
$o = new OptionParser(file => "$trunk/bin/pt-archiver");
|
|
$o->get_specs();
|
|
$o->get_opts();
|
|
|
|
my $exit_status = 0;
|
|
($output, $exit_status) = full_output(
|
|
sub { $o->usage_or_errors("$trunk/bin/pt-archiver"); },
|
|
);
|
|
|
|
is(
|
|
$exit_status,
|
|
1,
|
|
"Non-zero exit status on error parsing options (bug 1039074)"
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# --set-vars/set_vars()
|
|
# #############################################################################
|
|
|
|
@ARGV = qw();
|
|
$o = new OptionParser(file => "$trunk/bin/pt-archiver");
|
|
$o->get_specs();
|
|
$o->get_opts();
|
|
|
|
my $vars = $o->set_vars("$trunk/bin/pt-archiver");
|
|
is_deeply(
|
|
$vars,
|
|
{
|
|
wait_timeout => {
|
|
default => 1,
|
|
val => '10000',
|
|
},
|
|
},
|
|
"set_vars(): 1 default from docs"
|
|
) or diag(Dumper($vars));
|
|
|
|
# Bug 1182856: Zero values causes "Invalid --set-vars value: var=0"
|
|
@ARGV = qw(--set-vars SQL_LOG_BIN=0);
|
|
$o->get_opts();
|
|
|
|
$vars = $o->set_vars("$trunk/bin/pt-archiver");
|
|
is_deeply(
|
|
$vars,
|
|
{
|
|
wait_timeout => {
|
|
default => 1,
|
|
val => '10000',
|
|
},
|
|
SQL_LOG_BIN => {
|
|
default => 0,
|
|
val => '0',
|
|
},
|
|
},
|
|
"set_vars(): var=0 (bug 1182856)"
|
|
) or diag(Dumper($vars));
|
|
|
|
|
|
# #############################################################################
|
|
# https://bugs.launchpad.net/percona-toolkit/+bug/1199589
|
|
# pt-archiver deletes data despite --dry-run
|
|
# #############################################################################
|
|
|
|
# From the issue: "One problem is that --optimize is not being used correctly:
|
|
# the option takes an argument: d, s, or ds (see --analyze). The real problem
|
|
# is that --optimize is consuming the next option, which is --dry-run in this
|
|
# case. This shouldn't happen; it means the option parser is failing to notice
|
|
# that --dry-run is not the string val to --optimize but rather an option;
|
|
# it should catch this and the tool should fail to start with an error like
|
|
# "--optimize requires a value".
|
|
|
|
@ARGV = qw(--optimize --dry-run --ascend-first --where 1=1 --purge --source localhost);
|
|
$o = new OptionParser(file => "$trunk/bin/pt-archiver");
|
|
$o->get_specs();
|
|
$o->get_opts();
|
|
|
|
$output = output(
|
|
sub { $o->usage_or_errors(undef, 1); },
|
|
);
|
|
|
|
like(
|
|
$output,
|
|
qr/--optimize requires a string value/,
|
|
"String opts don't consume the next opt (bug 1199589)"
|
|
);
|
|
|
|
is(
|
|
$o->get('optimize'),
|
|
undef,
|
|
"--optimize didn't consume --dry-run (bug 1199589)"
|
|
);
|
|
|
|
@ARGV = qw(--optimize ds --dry-run --ascend-first --where 1=1 --purge --source localhost);
|
|
$o->get_opts();
|
|
|
|
$output = output(
|
|
sub { $o->usage_or_errors(undef, 1); },
|
|
);
|
|
|
|
is(
|
|
$output,
|
|
'',
|
|
"String opts still work (bug 1199589)"
|
|
);
|
|
|
|
is(
|
|
$o->get('optimize'),
|
|
'ds',
|
|
"--optimize got its value (bug 1199589)"
|
|
);
|
|
|
|
|
|
# #############################################################################
|
|
# Issue 1290911 : prompt_noecho should send prompt to STDERR so user can
|
|
# direct STDOUT to a file and still see the prompt
|
|
# #############################################################################
|
|
|
|
SKIP: {
|
|
eval {require Term::ReadKey};
|
|
skip ('\'prompt_no_echo outputs to STDERR\' because Term::ReadKey not available', 1) if $EVAL_ERROR;
|
|
|
|
$o = new OptionParser();
|
|
|
|
$output = output(
|
|
sub {
|
|
my $input = "thepassword";
|
|
local *STDIN;
|
|
open STDIN, '<', \$input;
|
|
$o->prompt_noecho('Test Prompt:'); },
|
|
|
|
stderr=>1,
|
|
);
|
|
|
|
is (
|
|
$output,
|
|
"Test Prompt:\n",
|
|
'prompt_no_echo outputs prompt to STDERR'
|
|
);
|
|
|
|
} # end skip
|
|
|
|
# #############################################################################
|
|
# Issue 1361293: Global config file with no-version-check option makes tools
|
|
# that don't recognize the option fail.
|
|
# #############################################################################
|
|
diag(`echo "no-version-check" > ~/.OptionParser.t.conf`);
|
|
$o = new OptionParser(
|
|
description => 'OptionParser.t parses command line options.',
|
|
usage => "$PROGRAM_NAME <options>"
|
|
);
|
|
$o->get_specs("$trunk/bin/pt-slave-find"), # doesn't have version-check option
|
|
$output = output(
|
|
sub {
|
|
$o->get_opts();
|
|
},
|
|
stderr=>1
|
|
);
|
|
unlike(
|
|
$output,
|
|
qr/Unknown option: no-version-check/,
|
|
'no-version-check ignored if unsupported and in config file. issue 1361293'
|
|
);
|
|
diag(`rm -rf ~/.OptionParser.t.conf`);
|
|
|
|
|
|
|
|
# #############################################################################
|
|
# Done.
|
|
# #############################################################################
|
|
{
|
|
local *STDERR;
|
|
open STDERR, '>', \$output;
|
|
$o->_d('Complete test coverage');
|
|
}
|
|
like(
|
|
$output,
|
|
qr/Complete test coverage/,
|
|
'_d() works'
|
|
);
|
|
|
|
done_testing;
|
|
exit;
|
|
|
|
|
|
|