The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;

# Gtest is a small package used to test the AI::Gene::Sequence
# package.  It provides a generate_token method and a seed_gene
# method, the first is highly deterministic (so tests of a module
# which hinge on randomness can work) and the second sets up a gene
# ready for a test.

# Also is a new method, which creates the gene and seeds it and
# 'd' and 'g' methods, which return (stringified) versions of the
# sequence ($self->[0]) and gene (@{$self->[1]}) respectively.

package GTest;
our (@ISA);
use AI::Gene::Sequence;
@ISA = qw(AI::Gene::Sequence);

sub new {
  my $class = shift;
  my $self = ['',[]];
  bless $self, $class;
  $self->seed_gene;
  return $self;
}

sub seed_gene {
  my $self = shift;
  $self->[0] = join('', 'a'..'j');
  @{$self->[1]} = ('a'..'j');
  return 1;
}

sub generate_token {
  my $self = shift;
  my ($type, $prev) = @_;
  $type ||= 'n';
  $prev = uc $type;
  return ($type, $prev);
}

sub d {
  my $self = shift;
  return $self->[0];
}

sub g {
  my $self = shift;
  return join('', @{$self->[1]});
}

package main;
use Test;
# see above for a small package ( GTest ) used to test G::G::S
BEGIN {plan tests => 111, todo =>[]}
my $hammer = 30; # set big to bash at methods with randomness

{ # test1
  # first of all, does our testing package behave
  my $gene = GTest->new;
  die "$0: Broken render" unless $gene->d eq 'abcdefghij'
                            and $gene->g eq 'abcdefghij';
  die "$0: Broken generate" unless ($gene->generate_token('a'))[1] eq 'A'
    and ($gene->generate_token())[0] eq 'n';
  ok(1);
}
my $main = GTest->new;
{ print "# clone\n";
  my $gene = $main->clone;
  ok($gene->g, $main->g);
}

{ print "# mutate_minor\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_minor(1);
  ok ($rt, 1); # return value
  ok ($gene->g ne $main->g); # changed
  $gene = $main->clone;
  $gene->mutate_minor(1,0);
  ok ($gene->g, 'Abcdefghij');
  $rt = $gene->mutate_minor(1,10); # outside of gene
  ok ($rt,0);
  ok ($gene->g, 'Abcdefghij');
  # hammer randomness, check for errors
  $rt = 0;
  for (1..$hammer) {
    eval '$gene->mutate_minor()';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_major\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_major(1,0);
  ok($rt, 1);
  ok($gene->g, 'Nbcdefghij');
  $gene = $main->clone;
  $gene->mutate_major;
  ok($gene->g ne $main->g, 1);
  $gene = $main->clone;
  $rt = $gene->mutate_major(1,10); # outside of gene
  ok($rt,0);
  ok($gene->g eq $main->g);
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    eval '$gene->mutate_major()';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_remove\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_remove(1,0);
  ok($rt,1);
  ok($gene->g eq 'bcdefghij' and $gene->d eq 'bcdefghij');
  $rt = $gene->mutate_remove(1,0,2);
  ok($rt,1);
  ok($gene->g eq 'defghij' and $gene->d eq 'defghij');
  $rt = $gene->mutate_remove(1,7); # outside of gene
  ok($rt,0);
  ok($gene->g eq 'defghij');
  $rt = $gene->mutate_remove(1,5,5); # extends beyond gene
  ok($rt,1);
  ok($gene->g eq 'defgh');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_remove(1,undef,0)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_insert\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_insert(1,0);
  ok($rt,1);
  ok($gene->g eq 'Nabcdefghij' and $gene->d eq 'nabcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_insert(1,10); # last possible pos
  ok($rt,1);
  ok($gene->d eq 'abcdefghijn' and $gene->g eq 'abcdefghijN');
  $gene = $main->clone;
  $rt = $gene->mutate_insert;
  ok($rt,1);
  ok($gene->d ne 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_insert(1,11); # outside of gene
  ok($rt,0);
  ok($gene->g eq 'abcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_insert';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_overwrite\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_overwrite(1,0,1); # first to second
  ok($rt,1);
  ok($gene->g, 'aacdefghij');
  ok($gene->d, 'aacdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_overwrite(1,0,4,3); # has length
  ok($rt,1);
  ok($gene->g, 'abcdabchij');
  ok($gene->d, 'abcdabchij');
  $gene = $main->clone;
  $rt = $gene->mutate_overwrite(1,3,4,3); # overlap
  ok($rt,1);
  ok($gene->g, 'abcddefhij');
  ok($gene->d, 'abcddefhij');
  $gene = $main->clone;
  $rt = $gene->mutate_overwrite(1,0,10,3); # dump lies at end of gene
  ok($rt,1);
  ok($gene->g, 'abcdefghijabc');
  ok($gene->d, 'abcdefghijabc');
  $gene = $main->clone;
  $rt = $gene->mutate_overwrite(1,0,11); # dump lies beyond end of gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  ok($gene->d, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_overwrite(1,11,4); # area to copy lies outside gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  ok($gene->d, 'abcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_overwrite(1,undef,undef,0)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_reverse\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_reverse(1,0,2);
  ok($rt,1);
  ok($gene->d, 'bacdefghij');
  ok($gene->g, 'bacdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_reverse(1,0,10); # whole gene
  ok($rt,1);
  ok($gene->d, 'jihgfedcba');
  ok($gene->g, 'jihgfedcba');
  $gene = $main->clone;
  $rt = $gene->mutate_reverse(1,8,4); # extends beyond gene
  ok($rt,0);
  ok($gene->d, 'abcdefghij');
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_reverse(1,10,1); # starts outside gene
  ok($rt,0);
  ok($gene->d, 'abcdefghij');
  ok($gene->g, 'abcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_reverse(1,undef,0)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate_duplicate\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_duplicate(1,0,0);
  ok($rt,1);
  ok($gene->g, 'aabcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_duplicate(1,9,0); # from end of gene to front
  ok($rt,1);
  ok($gene->g, 'jabcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_duplicate(1,10,0); # from outside of gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_duplicate(1,0,11); # to posn beyond end of gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_duplicate(1,0,10); # to posn at very end of gene
  ok($rt,1);
  ok($gene->g, 'abcdefghija');
  $gene = $main->clone;
  $rt = $gene->mutate_duplicate(1,0,10,10); # double the gene
  ok($rt,1);
  ok($gene->g, 'abcdefghijabcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_duplicate(1,undef,undef,0)';
  }
  ok($rt,0);
}

{ print "# mutate_switch\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_switch(1,0,9); # first and last
  ok($rt,1);
  ok($gene->g, 'jbcdefghia');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,0,8,2,2); # 1st 2 and last 2
  ok($rt,1);
  ok($gene->g, 'ijcdefghab');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,0,5,2,4); # different lengths
  ok($rt,1);
  ok($gene->g, 'fghicdeabj');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,0,10); # pos2 outside gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,10,0); # pos1 outside gene (silently same as)
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,0,9,1,2); # second section extends beyond
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_switch(1,0,2,5,3); # overlap of sections
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_switch(1,undef,undef,0,0)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}


{ print "# mutate_shuffle\n";
  my $gene = $main->clone;
  my $rt = $gene->mutate_shuffle(1,5,0); # from after to
  ok($rt,1);
  ok($gene->g, 'fabcdeghij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,5,0,2); # extended sequence
  ok($rt,1);
  ok($gene->g, 'fgabcdehij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,0,5,2); # to after from
  ok($rt,1);
  ok($gene->g, 'cdeabfghij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,0,9,1); # 1st to last
  ok($rt,1);
  ok($gene->g, 'bcdefghiaj');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,0,3,8); # overlap
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,0,10,1); # to posn outside gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,0,8,5); # should suceed
  ok($rt,1);
  ok($gene->g, 'fghabcdeij');
  $gene = $main->clone;
  $rt = $gene->mutate_shuffle(1,8,5,5); # extends beyond gene
  ok($rt,0);
  ok($gene->g, 'abcdefghij');
  # hammer randomness
  $rt = 0;
  for (1..$hammer) {
    $gene = $main->clone;
    eval '$gene->mutate_shuffle(1,undef,undef,0)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}

{ print "# mutate\n";
  my $rt = 0;
  # hammer with defaults
  for (1..$hammer) {
    my $gene = $main->clone;
    eval '$gene->mutate';
    $rt = 1 if $@;
  }
  ok($rt,0);
  # hammer with custom probs
  my %probs = (
               insert    =>1,
	       remove    =>1,
	       duplicate =>1,
	       overwrite =>1,
	       minor     =>1,
	       major     =>1,
	       switch    =>1,
	       shuffle   =>1,
	       );
  $rt = 0;
  for (1..$hammer) {
    my $gene= $main->clone;
    eval '$gene->mutate(1, \\%probs)';
    $rt = 1 if $@;
  }
  ok($rt,0);
}
1;