@ 11d698f · fetching
current
#!perl

use strict;
use warnings;

use Test::More tests => 178;
use lib 't';
use Util;

prep_environment();

my $file = 't/text/raven.txt';
my $word = 'nevermore';


# XXX Should also handle --files-with-matches and --files-without-matches.  See https://github.com/beyondgrep/ack3/issues/57
are_mutually_exclusive('-l', '-L', ['-l', '-L', $word, $file]);
for my $opt ( qw( -l -L ) ) {
    are_mutually_exclusive( $opt, '-o', [$opt, '-o', $word, $file] );
    are_mutually_exclusive( $opt, '--passthru', [$opt, '--passthru', $word, $file] );
    are_mutually_exclusive( $opt, '--output', [$opt, '--output', '$&', $word, $file] );
    are_mutually_exclusive( $opt, '--output', [$opt, '--output=$&', $word, $file] );
    are_mutually_exclusive( $opt, '-h', [$opt, '-h', $word, $file] );
    are_mutually_exclusive( $opt, '--with-filename', [$opt, '--with-filename', $word, $file] );
    are_mutually_exclusive( $opt, '--no-filename', [$opt, '--no-filename', $word, $file] );
    are_mutually_exclusive( $opt, '--column', [$opt, '--column', $word, $file] );
    are_mutually_exclusive( $opt, '-A', [$opt, '-A', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--after-context', [$opt, '--after-context', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--after-context', [$opt, '--after-context=1', $word, $file] );
    are_mutually_exclusive( $opt, '-B', [$opt, '-B', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--before-context', [$opt, '--before-context', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--before-context', [$opt, '--before-context=1', $word, $file] );
    are_mutually_exclusive( $opt, '-C', [$opt, '-C', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--context', [$opt, '--context', 1, $word, $file] );
    are_mutually_exclusive( $opt, '--context', [$opt, '--context=1', $word, $file] );
    are_mutually_exclusive( $opt, '--heading', [$opt, '--heading', $word, $file] );
    are_mutually_exclusive( $opt, '--break', [$opt, '--break', $word, $file] );
    are_mutually_exclusive( $opt, '--group', [$opt, '--group', $word, $file] );
    are_mutually_exclusive( $opt, '-f', [$opt, '-f', $file] );
    are_mutually_exclusive( $opt, '-g', [$opt, '-g', $word, $file] );
    are_mutually_exclusive( $opt, '--show-types', [$opt, '--show-types', $word, $file] );
}

# -o
are_mutually_exclusive( '-o', '--output',
    ['-o', '--output', '$&', $word, $file],
    ['-o', '--output=$&', $word, $file],
);
are_mutually_exclusive('-o', '-c', ['-o', '-c', $word, $file]);
are_mutually_exclusive('-o', '--count', ['-o', '--count', $word, $file]);
are_mutually_exclusive('-o', '--column', ['-o', '--column', $word, $file]);
are_mutually_exclusive('-o', '-A', ['-o', '-A', 1, $word, $file]);
are_mutually_exclusive('-o', '--after-context', ['-o', '--after-context', 1, $word, $file]);
are_mutually_exclusive('-o', '--after-context', ['-o', '--after-context=1', $word, $file]);
are_mutually_exclusive('-o', '-B', ['-o', '-B', 1, $word, $file]);
are_mutually_exclusive('-o', '--before-context', ['-o', '--before-context', 1, $word, $file]);
are_mutually_exclusive('-o', '--before-context', ['-o', '--before-context=1', $word, $file]);
are_mutually_exclusive('-o', '-C', ['-o', '-C', 1, $word, $file]);
are_mutually_exclusive('-o', '--context', ['-o', '--context', 1, $word, $file]);
are_mutually_exclusive('-o', '--context', ['-o', '--context=1', $word, $file]);
are_mutually_exclusive('-o', '-f', ['-o', '-f', $word, $file]);
are_mutually_exclusive('-o', '--passthru', ['-o', '--passthru', $word, $file]);

# --passthru
are_mutually_exclusive('--passthru', '--output', ['--passthru', '--output', '$&', $word, $file]);
are_mutually_exclusive('--passthru', '--output', ['--passthru', '--output=$&', $word, $file]);
are_mutually_exclusive('--passthru', '-m', ['--passthru', '-m', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--max-count', ['--passthru', '--max-count', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--max-count', ['--passthru', '--max-count=1', $word, $file]);
are_mutually_exclusive('--passthru', '-1', ['--passthru', '-1', $word, $file]);
are_mutually_exclusive('--passthru', '-c', ['--passthru', '-c', $word, $file]);
are_mutually_exclusive('--passthru', '--count', ['--passthru', '--count', $word, $file]);
are_mutually_exclusive('--passthru', '-A', ['--passthru', '-A', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--after-context', ['--passthru', '--after-context', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--after-context', ['--passthru', '--after-context=1', $word, $file]);
are_mutually_exclusive('--passthru', '-B', ['--passthru', '-B', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--before-context', ['--passthru', '--before-context', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--before-context', ['--passthru', '--before-context=1', $word, $file]);
are_mutually_exclusive('--passthru', '-C', ['--passthru', '-C', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--context', ['--passthru', '--context', 1, $word, $file]);
are_mutually_exclusive('--passthru', '--context', ['--passthru', '--context=1', $word, $file]);
are_mutually_exclusive('--passthru', '-f', ['--passthru', '-f', $word, $file]);
are_mutually_exclusive('--passthru', '-g', ['--passthru', '-g', $word, $file]);
are_mutually_exclusive('--passthru', '--column', ['--passthru', '--column', $word, $file]);
are_mutually_exclusive('--passthru', '-v', ['--passthru', '-v', $word, $file]);
are_mutually_exclusive('--passthru', '-o', ['--passthru', '-o', $word, $file]);
are_mutually_exclusive('--passthru', '--output', ['--passthru', '--output', $word, $file]);

# --output
for my $opt ( qw( -f -g -c --count ) ) {
    are_mutually_exclusive('--output', $opt,
        ['--output', '$&', $opt, $word, $file],
        ['--output=$&', $opt, $word, $file],
    );
}

are_mutually_exclusive('--output', '-A', ['--output=$&', '-A2', $word, $file]);
are_mutually_exclusive('--output', '-B', ['--output=$&', '-B2', $word, $file]);
are_mutually_exclusive('--output', '-C', ['--output=$&', '-C2', $word, $file]);
are_mutually_exclusive('--output', '--after-context', ['--output=$&', '--after-context=2', $word, $file]);
are_mutually_exclusive('--output', '--before-context', ['--output=$&', '--before-context=2', $word, $file]);
are_mutually_exclusive('--output', '--context', ['--output=$&', '--context=2', $word, $file]);

# --max-count
for my $opt ( qw( -1 -c -f -g ) ) {
    are_mutually_exclusive( '-m', $opt, ['-m', 1, $opt, $word, $file] );
    are_mutually_exclusive( '--max-count', $opt,
        ['--max-count', 1, $opt, $word, $file],
        ['--max-count=1', $opt, $word, $file],
    );
}

for my $opt ( qw( -h --no-filename ) ) {
    are_mutually_exclusive( $opt, '-f', [$opt, '-f', $word, $file] );
    are_mutually_exclusive( $opt, '-g', [$opt, '-g', $word, $file] );
}

# -H/--with-filename
for my $opt ( qw( -H --with-filename ) ) {
    are_mutually_exclusive( $opt, '-f', [ $opt, '-f', $word, $file] );
    are_mutually_exclusive( $opt, '-g', [ $opt, '-g', $word, $file] );
}

# -c/--count
for my $opt ( qw( -c --count ) ) {
    are_mutually_exclusive( $opt, '--column', [ $opt, '--column', $word, $file ] );
    are_mutually_exclusive( $opt, '-A', [ $opt, '-A', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '--after-context', [ $opt, '--after-context', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '-B', [ $opt, '-B', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '--before-context', [ $opt, '--before-context', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '-C', [ $opt, '-C', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '--context', [ $opt, '--context', 1, $word, $file ] );
    are_mutually_exclusive( $opt, '--heading', [ $opt, '--heading', $word, $file ] );
    are_mutually_exclusive( $opt, '--group', [ $opt, '--group', $word, $file ] );
    are_mutually_exclusive( $opt, '--break', [ $opt, '--break', $word, $file ] );
    are_mutually_exclusive( $opt, '-f', [ $opt, '-f', $word, $file ] );
    are_mutually_exclusive( $opt, '-g', [ $opt, '-g', $word, $file ] );
}

# -A/-B/-C/--after-context/--before-context/--context
for my $opt ( qw( -A -B -C --after-context --before-context --context ) ) {
    are_mutually_exclusive( $opt, '-f', [$opt, 1, '-f', $word, $file] );
    are_mutually_exclusive( $opt, '-g', [$opt, 1, '-g', $word, $file] );
    are_mutually_exclusive( $opt, '-p', [$opt, 1, '-p', $word, $file] );
}

# -f/-g
are_mutually_exclusive('-f', '-g', ['-f', '-g', $word, $file]);
for my $opt ( qw( -f -g ) ) {
    are_mutually_exclusive( $opt, '--group',   [$opt, '--group', $word, $file] );
    are_mutually_exclusive( $opt, '--heading', [$opt, '--heading', $word, $file] );
    are_mutually_exclusive( $opt, '--break',   [$opt, '--break', $word, $file] );
    are_mutually_exclusive( $opt, '--column',  [$opt, '--column', $word, $file] );
}

# -x
are_mutually_exclusive( '-x', '--files-from', ['-x', '--files-from', $word, $file] );
for my $opt ( qw( -f -g ) ) {
    are_mutually_exclusive( $opt, '-x',   [$opt, '-x', $word, $file] );
    are_mutually_exclusive( $opt, '--files-from', [$opt, '--files-from', $word, $file] );
}


subtest q{Verify that "options" that follow -- aren't factored into the mutual exclusivity} => sub {
    my ( $stdout, $stderr ) = run_ack_with_stderr('-A', 5, $word, $file, '--', '-l');
    ok(@{$stdout} > 0, 'Some lines should appear on standard output');
    is(scalar(@{$stderr}), 1, 'A single line should be present on standard error');
    like($stderr->[0], qr/No such file or directory/, 'The error message should indicate a missing file (-l is a filename here, not an option)');
    is(get_rc(), 0, 'The ack command should not fail');
};

subtest q{Verify that mutex options in different sources don't cause a problem} => sub {
    my $ackrc = <<'HERE';
--group
HERE

    my @stdout = run_ack('--count', $file, {
        ackrc => \$ackrc,
    });
    ok(@stdout > 0, 'Some lines should appear on standard output');
};

done_testing();

# Do this without system().
sub are_mutually_exclusive {
    local $Test::Builder::Level = $Test::Builder::Level + 1;

    my $opt1    = shift;
    my $opt2    = shift;
    my @argsets = @_;

    @argsets or die 'Must pass argsets';

    for my $argset ( @argsets ) {
        my @args = @{$argset};

        my ( $stdout, $stderr ) = run_ack_with_stderr(@args);

        subtest subtest_name( $opt1, $opt2, $argset ) => sub {
            plan tests => 4;

            isnt( get_rc(), 0, 'The ack command should fail' );
            is_empty_array( $stdout, 'No lines should be present on standard output' );
            is( scalar(@{$stderr}), 1, 'A single line should be present on standard error' );

            my $opt1_re = quotemeta($opt1);
            my $opt2_re = quotemeta($opt2);

            my $error = $stderr->[0] || ''; # avoid undef warnings
            if ( $error =~ /Options '$opt1_re' and '$opt2_re' can't be used together/ ||
                $error =~ /Options '$opt2_re' and '$opt1_re' can't be used together/ ) {

                pass( qq{Error message resembles "Options '$opt1' and '$opt2' can't be used together"} );
            }
            else {
                fail( qq{Error message does not resemble "Options '$opt1' and '$opt2' can't be used together"} );
                diag("Error message: '$error'");
            }
        };
    }

    return;
}