# -*- perl -*-
# t/01-new.t - Check constructor and methods called therewithin

use 5.14.0;
use warnings;
use Test::More;
use Capture::Tiny ':all';

BEGIN { use_ok( 'Perl5::Build::Warnings' ); }

my ($self, $file, $rv, $stdout, @stdout, $wg, $xg, $sf);
my ($warnings_count, $expected_groups_count, $expected_single_warning_count);
my ($expected_total_warnings_count);
my ($expected_total_warnings_in_source_count);

##### TESTS OF ERROR CONDITIONS #####

{
    local $@;
    eval { $self = Perl5::Build::Warnings->new(); };
    like($@, qr/Argument to constructor must be hashref/,
        "Got expected error message: no argument for new()");
}

{
    local $@;
    eval { $self = Perl5::Build::Warnings->new( 'file' => 'foo' ); };
    like($@, qr/Argument to constructor must be hashref/,
        "Got expected error message: argument for new() not a hashref");
}

{
    local $@;
    eval { $self = Perl5::Build::Warnings->new( [ 'file' => 'foo' ] ); };
    like($@, qr/Argument to constructor must be hashref/,
        "Got expected error message: argument for new() not a hashref");
}

{
    local $@;
    eval { $self = Perl5::Build::Warnings->new( { foo => 'bar' } ); };
    like($@, qr/Argument to constructor must contain 'file' element/,
        "Got expected error message: argument for new() must contain 'file' element");
}

{
    local $@;
    $file = 'bar';
    eval { $self = Perl5::Build::Warnings->new( { file => $file } ); };
    like($@, qr/Cannot locate $file/,
        "Got expected error message: cannot locate value for 'file' element");
}

##### TESTS OF VALID CODE #####

{
    note("regular (uncompressed) input");

    $file = "./t/data/make.g++-8-list-util-fallthrough.output.txt";
    $expected_groups_count = 7;
    $self = Perl5::Build::Warnings->new( { file => $file } );
    ok(defined $self, "Constructor returned defined object");
    isa_ok($self, 'Perl5::Build::Warnings');

    $stdout = capture_stdout {
        $self->report_warnings_groups;
    };
    like($stdout, qr/Wimplicit-fallthrough=\s+32/,
        "Reported implicit-fallthrough warning");

    @stdout = split /\n/ => $stdout;
    is(@stdout, $expected_groups_count,
        "report_warnings_groups(): $expected_groups_count types of warnings reported");
    my $w = $stdout[int(rand($expected_groups_count))];
    like($w, qr/^\s\s/, "report_warnings_groups() pretty prints with two leading whitespace");

    $wg = $self->get_warnings_groups;
    is(ref($wg), 'HASH', "get_warnings_groups() returned hashref");
    is(scalar keys %{$wg}, $expected_groups_count,
        "$expected_groups_count types of warnings found");
    $expected_single_warning_count = 32;
    is($wg->{'Wimplicit-fallthrough='}, $expected_single_warning_count,
        "Found $expected_single_warning_count instances of implicit-fallthrough warnings");
    $warnings_count = 0;
    map { $warnings_count += $_ } values %{$wg};
    $expected_total_warnings_count = 50;
    is($warnings_count, $expected_total_warnings_count,
        "Got total of $expected_total_warnings_count warnings");

    $xg = $self->get_warnings;
    is(ref($xg), 'ARRAY', "get_warnings() returned arrayref");
    is(scalar @{$xg}, $expected_total_warnings_count, "Got expected number of warnings");
}

{
    note("gzipped input");

    $file = "./t/data/linux.make.v5.29.3-26-g8da010579b.output.txt.gz";
    $expected_groups_count = 7;
    $self = Perl5::Build::Warnings->new( { file => $file } );
    ok(defined $self, "Constructor returned defined object");
    isa_ok($self, 'Perl5::Build::Warnings');

    $stdout = capture_stdout {
        $self->report_warnings_groups;
    };
    like($stdout, qr/Wimplicit-fallthrough=\s+33/,
        "Reported implicit-fallthrough warning");

    @stdout = split /\n/ => $stdout;
    is(@stdout, $expected_groups_count,
        "report_warnings_groups(): $expected_groups_count types of warnings reported");
    my $w = $stdout[int(rand($expected_groups_count))];
    like($w, qr/^\s\s/, "report_warnings_groups() pretty prints with two leading whitespace");

    $wg = $self->get_warnings_groups;
    is(ref($wg), 'HASH', "get_warnings_groups() returned hashref");
    is(scalar keys %{$wg}, $expected_groups_count,
        "$expected_groups_count types of warnings found");
    $expected_single_warning_count = 33;
    is($wg->{'Wimplicit-fallthrough='}, $expected_single_warning_count,
        "Found $expected_single_warning_count instances of implicit-fallthrough warnings");
    $warnings_count = 0;
    map { $warnings_count += $_ } values %{$wg};
    $expected_total_warnings_count = 51;
    is($warnings_count, $expected_total_warnings_count,
        "Got total of $expected_total_warnings_count warnings");

    $xg = $self->get_warnings;
    is(ref($xg), 'ARRAY', "get_warnings() returned arrayref");
    is(scalar @{$xg}, $expected_total_warnings_count, "Got expected number of warnings");
}

{
    note("Input generated by clang++");

    $file = "./t/data/clang++.make.output.txt.gz";
    $expected_groups_count = 7;
    $self = Perl5::Build::Warnings->new( { file => $file } );
    ok(defined $self, "Constructor returned defined object");
    isa_ok($self, 'Perl5::Build::Warnings');

    $stdout = capture_stdout {
        $self->report_warnings_groups;
    };
    like($stdout, qr/Wnull-pointer-arithmetic\s+189/,
        "Reported null-pointer-arithmetic warning");

    @stdout = split /\n/ => $stdout;
    is(@stdout, $expected_groups_count,
        "report_warnings_groups(): $expected_groups_count types of warnings reported");
    my $w = $stdout[int(rand($expected_groups_count))];
    like($w, qr/^\s\s/, "report_warnings_groups() pretty prints with two leading whitespace");

    $wg = $self->get_warnings_groups;
    is(ref($wg), 'HASH', "get_warnings_groups() returned hashref");
    is(scalar keys %{$wg}, $expected_groups_count,
        "$expected_groups_count types of warnings found");
    $expected_single_warning_count = 189;
    is($wg->{'Wnull-pointer-arithmetic'}, $expected_single_warning_count,
        "Found $expected_single_warning_count instances of null-pointer-arithmetic warnings");
    $warnings_count = 0;
    map { $warnings_count += $_ } values %{$wg};
    $expected_total_warnings_count = 293;
    is($warnings_count, $expected_total_warnings_count,
        "Got total of $expected_total_warnings_count warnings");

    $xg = $self->get_warnings;
    is(ref($xg), 'ARRAY', "get_warnings() returned arrayref");
    is(scalar @{$xg}, $expected_total_warnings_count, "Got expected number of warnings");

    {
        local $@;
        my $bad_group = q|duplicate-decl-specifier|;
        eval { $self->get_warnings_for_group($bad_group); };
        like($@, qr/Name of warnings group must begin with 'W'/,
            "Got expected error for warnings group name not starting with 'W'");
    }

    {
        local $@;
        my $bad_group = q|Wfoo-bar|;
        eval { $self->get_warnings_for_group($bad_group); };
        like($@, qr/Warnings group '$bad_group' not found/,
            "Got expected error for unknown or unrecorded warnings group");
    }

    $wg = "Wduplicate-decl-specifier";
    $rv = $self->get_warnings_for_group($wg);
    $expected_single_warning_count =  58;
    is(scalar @{$rv}, $expected_single_warning_count,
        "Got $expected_single_warning_count instances of warning '$wg'");

    $sf = 'op.c';
    $rv = $self->get_warnings_for_source($sf);
    $expected_total_warnings_in_source_count = 22;
    is(scalar @{$rv}, $expected_total_warnings_in_source_count,
        "Got $expected_total_warnings_in_source_count warnings from '$sf'");

}

{
    note("Input generated when using gcc-13 to run 'make' in a CPAN distribution");

=pod

  Wdeprecated-declarations                   9
  Wdiscarded-qualifiers                      1
  Wformat-overflow=                          5
  Wimplicit-int                              3
  Wincompatible-pointer-types                7
  Wint-to-pointer-cast                      42
  Wpointer-to-int-cast                      25
  Wstringop-overflow=                        1

    8 warnings categories

=cut

    $file = "./t/data/804.036-0-gcc-13.make.output.txt.gz";
    $expected_groups_count = 8;
    $self = Perl5::Build::Warnings->new( { file => $file } );
    ok(defined $self, "Constructor returned defined object");
    isa_ok($self, 'Perl5::Build::Warnings');

    $stdout = capture_stdout {
        $self->report_warnings_groups;
    };
    like($stdout, qr/Wint-to-pointer-cast\s+42/,
        "Reported int-to-pointer-cast warning");

    @stdout = split /\n/ => $stdout;
    is(@stdout, $expected_groups_count,
        "report_warnings_groups(): $expected_groups_count types of warnings reported");
    my $w = $stdout[int(rand($expected_groups_count))];
    like($w, qr/^\s\s/, "report_warnings_groups() pretty prints with two leading whitespace");

    $wg = $self->get_warnings_groups;
    is(ref($wg), 'HASH', "get_warnings_groups() returned hashref");
    is(scalar keys %{$wg}, $expected_groups_count,
        "$expected_groups_count types of warnings found");
    $expected_single_warning_count = 25;
    is($wg->{'Wpointer-to-int-cast'}, $expected_single_warning_count,
        "Found $expected_single_warning_count instances of Wpointer-to-int-cast warnings");
    $warnings_count = 0;
    map { $warnings_count += $_ } values %{$wg};
    $expected_total_warnings_count = 93;
    is($warnings_count, $expected_total_warnings_count,
        "Got total of $expected_total_warnings_count warnings");

    $xg = $self->get_warnings;
    is(ref($xg), 'ARRAY', "get_warnings() returned arrayref");
    is(scalar @{$xg}, $expected_total_warnings_count, "Got expected number of warnings");

    my $group = $self->{warnings}->[1]->{group};
    my $source = $self->{warnings}->[1]->{source};
    my $text = $self->{warnings}->[1]->{text};
    my $expected_group = "Wincompatible-pointer-types";
    my $expected_source = "Xlib.t";
    my $expected_text =
    "initialization of \xE2\x80\x98KeySym (*)(Display *, unsigned int,  int)\xE2\x80\x99 {aka \xE2\x80\x98long unsigned int (*)(Display *, unsigned int,  int)\xE2\x80\x99} from incompatible pointer type \xE2\x80\x98KeySym (*)(Display *, KeyCode,  int)\xE2\x80\x99 {aka \xE2\x80\x98long unsigned int (*)(Display *, unsigned char,  int)\xE2\x80\x99}";
    is($group, $expected_group, "Got warnings category '$expected_group' as expected");
    is($source, $expected_source, "Got source file '$expected_source' as expected");
    is($text, $expected_text, "Got expected text of build-time warning");

    my $expected_cleaned_text = qq|initialization of \"KeySym (*)(Display *, unsigned int,  int)\" {aka \"long unsigned int (*)(Display *, unsigned int,  int)\"} from incompatible pointer type \"KeySym (*)(Display *, KeyCode,  int)\" {aka \"long unsigned int (*)(Display *, unsigned char,  int)\"}|;

    my $cleaned_text;
    # https://github.com/jkeenan/perl5-build-warnings/pull/2/files
    #($cleaned_text) = $text =~ s/[\x{2018}\x{2019}]/"/gr;
    ($cleaned_text) = $text =~ s{(
        \xE2\x80\x98 |
        \xE2\x80\x99
    )}
    {"}grx;
    is($cleaned_text, $expected_cleaned_text,
        "'Smart' open and closed double-quotes converted to ordinary double-quotes");

}

done_testing();
