The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Test::Code::TidyAll::SVN;

use Capture::Tiny qw(capture_stdout capture_stderr capture);
use Code::TidyAll::SVN::Precommit;
use Code::TidyAll::SVN::Util qw(svn_uncommitted_files);
use Code::TidyAll::Util qw(tempdir_simple);
use Code::TidyAll;
use IPC::System::Simple qw(run);
use Path::Tiny qw(path);
use Test::Class::Most parent => 'Code::TidyAll::Test::Class';

my ( $precommit_hook_template, $tidyall_ini_template );

sub test_svn : Tests {
    my ($self) = @_;

    $self->require_executable('svn');

    my $temp_dir = tempdir_simple();
    my $repo_dir = $temp_dir->child('repo');
    my $src_dir  = $temp_dir->child('src');
    my $work_dir = $temp_dir->child('work');
    my $hook_log = $temp_dir->child('hook.log');
    my ( $stdout, $stderr );

    my $log_contains = sub {
        my $regex = shift;
        like( $hook_log->slurp, $regex );
    };

    my $clear_log = sub {
        run("cat /dev/null > $hook_log");
    };

    my $committed = sub {
        $stdout = capture_stdout { system( sprintf( 'svn status %s', $work_dir ) ) };
        unlike( $stdout, qr/\S/, "committed" );
    };

    my $uncommitted = sub {
        $stdout = capture_stdout { system( sprintf( 'svn status %s', $work_dir ) ) };
        like( $stdout, qr/^M/, "uncommitted" );
    };

    run("svnadmin create $repo_dir");
    my $hooks_dir = $repo_dir->child('hooks');
    ok( $hooks_dir->is_dir, "$hooks_dir exists" );

    $src_dir->mkpath(0775);
    $src_dir->child('foo.txt')->spew("abc");

    run( sprintf( 'svn -q import %s file://%s/myapp/trunk -m "import"', $src_dir,  $repo_dir ) );
    run( sprintf( 'svn -q checkout file://%s/myapp/trunk %s',           $repo_dir, $work_dir ) );

    my $foo_txt = $work_dir->child('foo.txt');
    is( $foo_txt->slurp, "abc", "checkout and import ok" );
    cmp_deeply( [ svn_uncommitted_files($work_dir) ], [], "no uncommitted files" );

    my $precommit_hook_file = $hooks_dir->child('pre-commit');
    my $precommit_hook = sprintf( $precommit_hook_template, path('lib')->realpath, $hook_log );
    $precommit_hook_file->spew($precommit_hook);
    $precommit_hook_file->chmod(0755);

    my $bar_dir = $work_dir->child('bar');
    $bar_dir->mkpath( { mode => 0755 } );
    $bar_dir->child('foo.txt')->spew("abc ");

    run( sprintf( 'svn -q add %s', $bar_dir ) );
    cmp_deeply( [ svn_uncommitted_files($work_dir) ], [ re("foo.txt") ], "one uncommitted file" );

    $stderr = capture_stderr {
        run( sprintf( 'svn -q commit -m "changed" %s %s', $foo_txt, $bar_dir ) );
    };
    unlike( $stderr, qr/\S/ );
    $log_contains->(qr|could not find.*upwards from 'myapp/trunk/foo.txt'|);
    $clear_log->();
    $committed->();
    cmp_deeply( [ svn_uncommitted_files($work_dir) ], [], "no uncommitted files" );

    my $tidyall_ini = $work_dir->child('tidyall.ini');
    $tidyall_ini->spew($tidyall_ini_template);
    run( sprintf( 'svn -q add %s', $tidyall_ini ) );
    cmp_deeply(
        [ svn_uncommitted_files($work_dir) ],
        [ re("tidyall.ini") ],
        "one uncommitted file"
    );
    run( sprintf( 'svn -q commit -m "added" %s', $tidyall_ini ) );

    $foo_txt->spew("abc");
    $stderr
        = capture_stderr { system( sprintf( 'svn -q commit -m "changed" %s', $foo_txt ) ) };
    like( $stderr, qr/1 file did not pass tidyall check/ );
    like( $stderr, qr/needs tidying/ );
    $uncommitted->();

    $foo_txt->spew("ABC");
    my $bar_dat = $work_dir->child('bar.dat');
    $bar_dat->spew('123');
    run( sprintf( 'svn -q add %s', $bar_dat ) );
    $stderr = capture_stderr {
        system( sprintf( 'svn -q commit -m "changed" %s %s', $foo_txt, $bar_dat ) );
    };
    unlike( $stderr, qr/\S/ );
    $committed->();

    $foo_txt->spew('def');
    $stderr = capture_stderr {
        system( sprintf( 'svn -q commit -m "NO TIDYALL - emergency fix!" %s', $foo_txt ) );
    };
    unlike( $stderr, qr/\S/ );
    $committed->();
}

$precommit_hook_template = '#!' . $^X . "\n" . <<'EOF';
use lib qw(%s);
use Code::TidyAll::SVN::Precommit;
use Log::Any::Adapter (File => "%s");
use strict;
use warnings;

Code::TidyAll::SVN::Precommit->check(
    extra_conf_files => ["perlcriticrc"],
    tidyall_options => { verbose => 1 }
);
EOF

$tidyall_ini_template = <<'EOF';
[+Code::TidyAll::Test::Plugin::UpperText]
select = **/*.txt
EOF