The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use warnings;
use strict;

use Test::More no_plan =>;

use Getopt::Base;

# sanity-check check
{
  eval {
    Getopt::Base->new(
      positional => [qw(input)],
      options => [],
    );
  };
  like($@, qr/positional 'input' is not an option/);
}
########################################################################

# empty option set, '--' termination
{
  my $go = Getopt::Base->new();
  ok($go);
  {
    my @arg = (1..3);
    my $o = $go->process(\@arg);
    ok($o);
    is_deeply(\@arg, [1..3], 'no options');
  }
  {
    my @arg = ('--', '--foo');
    $go->process(\@arg);
    is_deeply(\@arg, ['--foo'], '--');
  }
  {
    my @arg = (7, '--', '--foo');
    $go->process(\@arg);
    is_deeply(\@arg, [7, '--foo'], '--');
  }
}
########################################################################

# simple setup, positional support
{
  my $go = Getopt::Base->new(
    positional => [qw(input)],
    options => [
      input => {
        type => 'string',
      },
    ],
  );
  foreach my $set (
    ['--input', 'foo', 'bar'],
    ['foo', 'bar'],
    ['bar', '--input', 'foo'],
    ['foo', '--', 'bar'],
  ) {
    my @arg = @$set;
    my $o = $go->process(\@arg);
    is($o->input, 'foo', 'got --input');
    is_deeply(\@arg, ['bar']);
  }
}
########################################################################
# more with positionals
{
  my $go = Getopt::Base->new(
    positional => [qw(input output)],
    options => [
      input  => { type => 'string', },
      output => { type => 'string', },
    ],
  );
  {
    my @args = qw(foo bar);
    my $o = $go->process(\@args);
    is($o->input, 'foo');
    is($o->output, 'bar');
    is(scalar @args, 0);
  }
  {
    my @args = qw(foo);
    my $o = $go->process(\@args);
    is($o->input, 'foo');
    ok(! exists($o->{output}), 'no output key');
    is($o->output, undef);
    is(scalar @args, 0);
  }
}
########################################################################

# aliases, shortening
{
  my $go = Getopt::Base->new(
    positional => [qw(input)],
    options => [
      input => {
        aliases => ['something_something', 'extra_sausages'],
        short => ['q', 'r', 's'],
        type => 'string',
      },
    ],
  );
  foreach my $try (
    [qw(--input foo 42)],
    [qw(--in foo 42)],
    [qw(42 --something-something foo)],
    [qw(42 --extra-sausages foo)],
    [qw(--ex foo 42)],
    [qw(42 -q foo)],
    [qw(-q foo 42)],
    [qw(-r foo 42)],
    [qw(-s foo 42)],
    ) {
    my @pass = @$try;
    my $o = $go->process(\@pass);
    is($o->input, 'foo');
    is_deeply(\@pass, [42]);
  }
  my @args = qw(foo 42);
  my $o = $go->process(\@args);
  is($o->input, 'foo');
  is_deeply(\@args, [42]);
}
########################################################################

# boolean on/off
{
  my $go = Getopt::Base->new();
  $go->add_option(verbose => short => ['v'], default => 1);
  $go->add_aliases(no_verbose => ['q'], 'quiet', 'hush');
  foreach my $args (
    [],
    ['-v'],
    ['--verbose'],
    ['--ve'],
    ['--v'],
  ) {
    my $o = $go->process($args);
    ok($o->verbose);
  }
  foreach my $args (
    ['-q'],
    ['--hush'],
    ['--hu'],
    ['--qui'],
    ['--quiet'],
    ['--verbose', '--no-verbose'],
    ['--verbose', '--no-ve'],
  ) {
    my $o = $go->process($args);
    ok(! $o->verbose);
  }
}
########################################################################
# hashes and arrays
{
  my $go = Getopt::Base->new();
  $go->add_option(array => default => []);
  $go->add_option(also  => form => 'ARRAY');
  $go->add_option(ahash => default => {});
  my $o = $go->process([
    '--array', 'foo', '--ahash', 'x=y',
    '--array', 'y', '--ahash', 'y=x',
    '--also', 7, '--also', 8,
  ]);
  is_deeply([$o->array], [foo => y =>]);
  is_deeply({$o->ahash}, {x => y => y => x =>});
  is_deeply([$o->also], [7,8]);
}
########################################################################
# isa
{
  my $did_req;
  my $source = 'package xthbbt; sub new {return join("|", @_, 19)}; 1;';
  local @INC = (
    sub {
      my ($code, $mod) = @_;
      return unless($mod =~ m/^xthbbt\.pm$/);
      $did_req = 1;
      open(my $fh, '<', \$source) or die $!;
      return($fh);
    },
    @INC
  );
  my $go = Getopt::Base->new(
    options => [xx => { type => 'string', isa => 'xthbbt' }]
  );
  my $o = $go->process(my $args = ['--xx', 'foo']);
  ok($o);
  is(scalar(@$args), 0);
  ok($did_req, 'require ok');
  is($o->xx, 'xthbbt|foo|19');
  delete($INC{'xthbbt.pm'});
}

########################################################################
# errors
{
  eval {Getopt::Base->new(
    positional => [qw(input)],
    options => [ thing => { type => 'boolean', }, ],
  )};
  like($@, qr/^positional 'input' is not an option/);

  eval {Getopt::Base->new(
    positional => [qw(thing)],
    options => [ thing => { type => 'boolean', }, ],
  )};
  like($@, qr/^positional 'thing' cannot be a boolean/);

  eval {Getopt::Base->new(
    options => [ -thing => { }, ],
  )};
  like($@, qr/^options cannot contain dashes \('-thing'\)/);

  eval {Getopt::Base->new(
    options => [ thing => { aliases => ['--foo']}, ],
  )};
  like($@, qr/^aliases cannot contain dashes \('--foo'\)/);

  eval {Getopt::Base->new(
    options => [thing => {short => ['x', 'yyy']}, ],
  )};
  like($@, qr/^short options must be only one character \('yyy'\)/);

  {
    my $go = Getopt::Base->new(
      options => [xx => { type => 'string', isa => 'xthbbt' }]
    );
    eval { $go->process(my $args = ['--xx', 'foo']); };
    like($@, qr/Can't locate xthbbt\.pm/);
  }
}

########################################################################

ok(Getopt::Base->new(
  positional => [qw(deal)],
  options => [
    thing => {
      short     => ['t'],
      type      => 'boolean',
      default   => 0,
    },
    deal  => {
      short   => ['d'],
      type    => 'string',
      default => '',
    },
    stuff => {
      type    => 'string',
      default => [],
    },
    things => {
      type    => 'string',
      default => {},
    },
  ],
));

# vim:ts=2:sw=2:et:sta