#! /usr/bin/perl -w

# Test suite for config2struct
# Usage:
#     ./run             # run all tests
#     ./run 1 3 5       # run specified tests
#     ./run -l          # list all tests


use Test::More;
use Getopt::Long;


my ($coverage);
GetOptions(
    'cover' => \$coverage,
    'list' => \$list_tests,
);


my @tests = (
    {
        schema => 'in1.sch',
        data => 'in1.data',
        parser => 'parser.c',
        cmd => '-F in1.data',
        out => 'out1a.txt',
        desc => "documentation example"
    },
    {
        schema => 'in1.sch',
        data => 'in1.data',
        parser => 'parser.c',
        cmd => '-F in1.data --size 800,600',
        out => 'out1b.txt',
        desc => "Compound option with no override"
    },
    {
        schema => 'in1.sch',
        data => 'in1.data',
        parser => 'parser.c',
        cmd => '-F in1.data --book "Snow Crash","Neal Stephenson",12,3.14',
        out => 'out1c.txt',
        desc => "Compound option with override",
    },
    {
        schema => 'in2.sch',
        data => 'in2a.data',
        parser => 'parser.c',
        cmd => '-F in2a.data',
        out => 'out2a.txt',
        desc => "Basic with defaults",
    },
    {
        schema => 'in2.sch',
        data => 'in2b.data',
        parser => 'parser.c',
        cmd => '-F in2b.data',
        out => 'out2b.txt',
        desc => "Override defaults",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data',
        out => 'out2c.txt',
        desc => "Set optional settings",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --mymbool --mymint 23 --mymfloat 2.76 --mymstring "hail Eris"',
        out => 'out2c1.txt',
        desc => "Command-line override of simple settings",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --group1 1,23,"hello world"',
        out => 'out2d.txt',
        desc => "Compound option for group",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --list-edit 1,23,"hello world"',
        out => 'out2e.txt',
        desc => "Compound option for list, append one",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --list-edit 1,23,"hello world" --list-edit 2,42,"bye" --list-edit 3,6,"cruel world"',
        out => 'out2ea.txt',
        desc => "Compound option for list, append several",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --list-edit 1,23,second',
        out => 'out2f.txt',
        desc => "Compound option for list, replace",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --list-edit 1,23,second --list-edit 2,42,"bye"',
        out => 'out2fa.txt',
        desc => "Compound option for list, replace and add",
    },
    {
        schema => 'in2.sch',
        data => 'in2c.data',
        parser => 'parser.c',
        cmd => '-F in2c.data --list_add 1,23',
        out => 'out2g.txt',
        desc => "Compound option for list, append with inited string",
    },
    {
        schema => 'in3.sch',
        data => 'in2d.data',
        parser => 'parser.c',
        cmd => '--myint 42 --list-edit 1,23,"hello world" --list-edit 0,24,"good bye world"',
        out => 'out2h.txt',
        desc => "No config file, add to list",
    },
    {
        schema => 'in3.sch',
        data => 'in2d.data',
        parser => 'parser.c',
        cmd => '-i 2302 -I 2032 -s "hello"',
        out => 'out2i.txt',
        desc => "No config file, short options",
    },
    {
        schema => 'in4.sch',
        data => 'in2d.data',
        parser => 'parser.c',
        cmd => '-i 2302 -I 2032 -s "hello"',
        out => 'out4.txt',
        desc => "Robustness: schema contains wrong type",
        rb_perl => 1,  # Robustness test for Perl code
    },
    {
        schema => 'in5.sch',
        data => 'in5a.data',
        parser => 'parser.c',
        cmd => '-F in5a.data',
        out => 'out5a1.txt',
        desc => "Config file with dash/underscore adapts well",
    },
    {
        schema => 'in5.sch',
        data => 'in5a.data',
        parser => 'parser.c',
        cmd => '-F in5a.data --my-int1 22',
        out => 'out5a2.txt',
        desc => "Command-line with dash sets underscore field",
    },
);

# Number tests
my $i = 1;
map { $_->{'index'} = $i++ } @tests;

# Only keep selected tests
@tests = @tests[ map { $_ - 1 } @ARGV ] if @ARGV;

die "No test specified?\n" unless defined $tests[0];

#####List all tests
format test_list_top =
ID  | Description
----+-------------------------------------------------------------------------
.
format test_list =
@>> | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$_->{'index'}, $_->{'desc'};
.
if ($list_tests) {
    format_name STDOUT "test_list";
    format_top_name STDOUT "test_list_top";
    map { write; } @tests;
    exit 0;
}
#####/list

sub prep_test_dir {
    my ($test) = @_;
    my $tdir = "tmp_$test->{schema}";
    -d $tdir or mkdir "$tdir" or die "mkdir $tdir: $!\n"; 
    `cp -n ../argtable3.h $tdir`;
    `cp -n ../argtable3.c $tdir`;
    `cp -n Makefile $tdir`;
    `cp -n $test->{schema} $tdir`;
    `cp -n $test->{data} $tdir`;
    `cp -n $test->{parser} $tdir/parser.c`;
    return $tdir;
}


`rm -rfv tmp_*`;
`cover -delete` if $coverage;
die "Unable to clear coverage analysis.\n" if $?;

my @results;
my $tnum = 1;
foreach my $test (@tests) {
    my $dir = prep_test_dir($test);
    chdir $dir or die "chdir $dir: $!\n";

    # Robustness tests for the Perl scrupt result in make
    # failing, so we ignore make result ($res_make), and don't
    # run the binary (which won't have made) so ignore
    # $res_run, and only test the output of the Perl code
    # ($res_diff)
    my $rbp = $test->{rb_perl};

    my $opts = "";
    $opts .= '-MDevel::Cover="-silent,on,-db,../cover_db"' if $coverage;

    # $res variables are 0 on success, 1 on failure
    my ($res_make, $res_run, $res_diff);
    my $make = "SCHEMA=$test->{schema} PARSER=$test->{parser} PERLOPTS=$opts make";
    $make .= " > $test->{out} 2>&1" if $rbp;
    $res_make = system($make);

    my $cmd;
    if ($rbp) {
        $res_make = 0;
        $res_run = 0;
    } else {
        ok($res_make == 0, "$test->{desc} makes correctly");
        if ($res_make) {
            diag("cmd: `$make`");
        }

        $cmd = "stdbuf -o0 -e0 ./parser $test->{cmd} > $test->{out} 2>&1";
        $res_run = system($cmd);
        ok($res_run == 0, "$test->{desc} runs");
    }

    $res_diff = system("diff -u ../$test->{out} $test->{out}");
    ok($res_diff == 0, "$test->{desc} returns correctly");
    if ($res_run || $res_diff) {
        diag("cmd: `$cmd`");
    }

    print "make:$res_make run:$res_run diff:$res_diff\n";
    push @results, [$test->{index}, $test->{desc}, ($res_make or $res_run or $res_diff)];

    `lcov --directory . --capture --output-file conf2struct.info`;
    `genhtml conf2struct.info`;

    chdir "..";
    $tnum++;
}


done_testing();

if ($coverage) {
    system "cover"; 
    die "Unable to generate coverage status.\n" if $?;
    warn "Coverage report generated in cover_db/coverage.html\n";
}



format test_results_top =
ID  | Description                                                       | Status
----+-------------------------------------------------------------------+-------
.

format test_results = 
@>> | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |   @>>
$_->[0], $_->[1], $_->[2] ? "NOK" : "OK"
.

format_name STDOUT "test_results";
format_top_name STDOUT "test_results_top";

map { write; } @results;

