diff --git a/META.json b/META.json index fc579a05..70ccac25 100644 --- a/META.json +++ b/META.json @@ -50,7 +50,6 @@ "Test::NoWarnings" : "1.04", "Test::Output" : "1.03", "Test::Simple" : "1.001002", - "Test::Spec" : "0.49", "Test::TempDir::Tiny" : "0.016" } } diff --git a/META.yml b/META.yml index 16fb4e79..2d5cbf97 100644 --- a/META.yml +++ b/META.yml @@ -13,7 +13,6 @@ build_requires: Test::NoWarnings: '1.04' Test::Output: '1.03' Test::Simple: '1.001002' - Test::Spec: '0.49' Test::TempDir::Tiny: '0.016' configure_requires: Module::Build::Tiny: '0.039' diff --git a/cpanfile b/cpanfile index 488dba0f..7571b47d 100644 --- a/cpanfile +++ b/cpanfile @@ -19,7 +19,6 @@ on test => sub { requires 'Test::NoWarnings' => '1.04'; requires 'Test::Output' => '1.03'; requires 'Test::Simple' => '1.001002'; - requires 'Test::Spec' => '0.49'; requires 'Test::TempDir::Tiny' => '0.016'; requires 'Test2::V0' => '0.000163'; requires 'Test2::Plugin::NoWarnings' => '0.10'; diff --git a/t/app-perlbrew-path-installation.t b/t/app-perlbrew-path-installation.t index 68e05103..6b6f3b52 100644 --- a/t/app-perlbrew-path-installation.t +++ b/t/app-perlbrew-path-installation.t @@ -1,12 +1,9 @@ #!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; use File::Temp qw[]; -use Test::Spec; -use Test::Deep; - use App::Perlbrew::Path::Root; use App::Perlbrew::Path::Installation; use App::Perlbrew::Path::Installations; @@ -19,30 +16,27 @@ sub arrange_installation; describe "App::Perlbrew::Path::Root" => sub { describe "perls()" => sub { - context "without parameters" => sub { + describe "without parameters" => sub { it "should return Instalations object" => sub { local $ENV{HOME}; my $path = arrange_root->perls; - - cmp_deeply $path, looks_like_perl_installations("~/.root/perls"); + is $path, looks_like_perl_installations("~/.root/perls"); }; }; - context "with one parameter" => sub { + describe "with one parameter" => sub { it "should return Installation object" => sub { local $ENV{HOME}; my $path = arrange_root->perls('blead'); - - cmp_deeply $path, looks_like_perl_installation("~/.root/perls/blead"); + is $path, looks_like_perl_installation("~/.root/perls/blead"); }; }; - context "with multiple paramters" => sub { + describe "with multiple paramters" => sub { it "should return Path object" => sub { local $ENV{HOME}; my $path = arrange_root->perls('blead', '.version'); - - cmp_deeply $path, looks_like_path("~/.root/perls/blead/.version"); + is $path, looks_like_path("~/.root/perls/blead/.version"); }; } }; @@ -58,7 +52,7 @@ describe "App::Perlbrew::Path::Installations" => sub { my @list = $root->perls->list; - cmp_deeply \@list, [ + is \@list, [ looks_like_perl_installation("~/.root/perls/perl-1"), looks_like_perl_installation("~/.root/perls/perl-2"), ]; @@ -71,27 +65,24 @@ describe "App::Perlbrew::Path::Installation" => sub { it "should return installation name" => sub { local $ENV{HOME}; my $installation = arrange_installation('foo-bar'); - - cmp_deeply $installation->name, 'foo-bar'; + is $installation->name, 'foo-bar'; }; it "should provide path to perl" => sub { local $ENV{HOME}; my $perl = arrange_installation('foo-bar')->perl; - - cmp_deeply $perl->stringify_with_tilde, '~/.root/perls/foo-bar/bin/perl'; + is $perl->stringify_with_tilde, '~/.root/perls/foo-bar/bin/perl'; }; it "should provide path to version file" => sub { local $ENV{HOME}; my $file = arrange_installation('foo-bar')->version_file; - - cmp_deeply $file->stringify_with_tilde, '~/.root/perls/foo-bar/.version'; + is $file->stringify_with_tilde, '~/.root/perls/foo-bar/.version'; }; }; }; -runtests unless caller; +done_testing; sub looks_like_path { my ($path, @tests) = @_; @@ -101,19 +92,18 @@ sub looks_like_path { : 'stringify' ; - all( - methods($method => $path), - Isa('App::Perlbrew::Path'), - @tests, - ); + object { + call $method => $path; + prop isa => 'App::Perlbrew::Path'; + }; } sub looks_like_perl_installation { - looks_like_path(@_, Isa('App::Perlbrew::Path::Installation')); + looks_like_path(@_, object { prop isa => 'App::Perlbrew::Path::Installation' }); } sub looks_like_perl_installations { - looks_like_path(@_, Isa('App::Perlbrew::Path::Installations')); + looks_like_path(@_, object { prop isa => 'App::Perlbrew::Path::Installation' }); } sub arrange_root { diff --git a/t/command-alias.t b/t/command-alias.t index b2178b63..62b5e894 100644 --- a/t/command-alias.t +++ b/t/command-alias.t @@ -1,16 +1,14 @@ #!/usr/bin/env perl -#!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; BEGIN { $ENV{SHELL} = "/bin/bash" } use FindBin; use lib $FindBin::Bin; use App::perlbrew; -require "test_helpers.pl"; +require "test2_helpers.pl"; -use Test::Spec; use Test::Output; use Config; @@ -18,7 +16,7 @@ mock_perlbrew_install("perl-5.14.1"); mock_perlbrew_lib_create('perl-5.14.1@nobita'); describe "alias command," => sub { - before each => sub { + before_each 'cleanup env' => sub { delete $ENV{PERL_MB_OPT}; delete $ENV{PERL_MM_OPT}; delete $ENV{PERL_LOCAL_LIB_ROOT}; @@ -44,4 +42,4 @@ describe "alias command," => sub { }; }; -runtests unless caller; +done_testing; diff --git a/t/command-available.t b/t/command-available.t index 1d735300..d9223925 100644 --- a/t/command-available.t +++ b/t/command-available.t @@ -1,7 +1,6 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::Spec; +use Test2::V0; +use Test2::Tools::Spec; use File::Temp qw( tempdir ); use Test::Output; @@ -28,13 +27,18 @@ my %available_perl_dists = ( sub mocked_perlbrew { my $app = App::perlbrew->new( @_ ); - $app->expects( 'available_perl_distributions' )->returns( \%available_perl_dists ); - return $app; + + my $mock = mock $app, + override => [ + available_perl_distributions => sub { \%available_perl_dists } + ]; + + return ($mock, $app); } describe "available command output, when nothing installed locally," => sub { it "should display a list of perl versions" => sub { - my $app = mocked_perlbrew( "available", "--verbose" ); + my ($mock, $app) = mocked_perlbrew( "available", "--verbose" ); stdout_like sub { $app->run(); @@ -60,14 +64,16 @@ describe "available command output, when nothing installed locally," => sub { describe "available command output, when something installed locally," => sub { it "should display a list of perl versions, with markers on installed versions" => sub { - my $app = mocked_perlbrew( "available", "--verbose" ); + my ($mock, $app) = mocked_perlbrew( "available", "--verbose" ); my @installed_perls = ( { name => "perl-5.24.0" }, { name => "perl-5.20.3" } ); - $app->expects("installed_perls")->returns(@installed_perls); + $mock->override( + "installed_perls" => sub { @installed_perls } + ); stdout_like sub { $app->run(); @@ -94,4 +100,4 @@ describe "available command output, when something installed locally," => sub { }; }; -runtests unless caller; +done_testing; diff --git a/t/command-clone-modules.t b/t/command-clone-modules.t index dacd2678..1c62a9e0 100644 --- a/t/command-clone-modules.t +++ b/t/command-clone-modules.t @@ -1,15 +1,13 @@ #!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; BEGIN { $ENV{SHELL} = "/bin/bash" } use FindBin; use lib $FindBin::Bin; use App::perlbrew; -require "test_helpers.pl"; - -use Test::Spec; +require "test2_helpers.pl"; mock_perlbrew_install("perl-5.14.1"); mock_perlbrew_install("perl-5.16.0"); @@ -33,7 +31,7 @@ sub App::perlbrew::run_command_exec { use warnings; describe "clone-modules command," => sub { - before each => sub { + before_each 'cleanup env' => sub { delete $ENV{PERL_MB_OPT}; delete $ENV{PERL_MM_OPT}; delete $ENV{PERL_LOCAL_LIB_ROOT}; @@ -71,26 +69,30 @@ describe "clone-modules command," => sub { describe "when invoked with one argument X", sub { it "should display clone modules from current-perl to X", sub { my $app = App::perlbrew->new("clone-modules", "perl-5.14.1"); - $app->expects("current_env")->returns("perl-5.16.0")->at_least(1); + + my $mock = mocked($app)->expects("current_env")->returns("perl-5.16.0")->at_least(1); $app->run; is $__from, "perl-5.16.0"; is $__to, "perl-5.14.1"; ok(!defined($__notest)); + + $mock->verify; }; }; describe "when invoked with one argument X, with `--notest`", sub { it "should display clone modules from current-perl to X", sub { my $app = App::perlbrew->new("clone-modules", "--notest", "perl-5.14.1"); - $app->expects("current_env")->returns("perl-5.16.0")->at_least(1); + my $mock = mocked($app)->expects("current_env")->returns("perl-5.16.0")->at_least(1); $app->run; is $__from, "perl-5.16.0"; is $__to, "perl-5.14.1"; ok(defined($__notest)); + $mock->verify; }; }; }; -runtests unless caller; +done_testing; diff --git a/t/command-compgen.t b/t/command-compgen.t index 442d4a8f..d4affa3c 100644 --- a/t/command-compgen.t +++ b/t/command-compgen.t @@ -1,13 +1,12 @@ #!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; use FindBin; use lib $FindBin::Bin; use App::perlbrew; -require "test_helpers.pl"; +require "test2_helpers.pl"; -use Test::Spec; use Test::Output qw( stdout_from ); $ENV{PERLBREW_DEBUG_COMPLETION} = 0; @@ -84,5 +83,4 @@ describe "compgen command," => sub { } }; -runtests unless caller; - +done_testing; diff --git a/t/command-env.t b/t/command-env.t index d4ec69d3..1be3e4e5 100644 --- a/t/command-env.t +++ b/t/command-env.t @@ -1,15 +1,14 @@ #!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; BEGIN { $ENV{SHELL} = "/bin/bash" } use FindBin; use lib $FindBin::Bin; use App::perlbrew; -require "test_helpers.pl"; +require "test2_helpers.pl"; -use Test::Spec; use Test::Output; use Config; @@ -17,7 +16,7 @@ mock_perlbrew_install("perl-5.14.1"); mock_perlbrew_lib_create('perl-5.14.1@nobita'); describe "env command," => sub { - before each => sub { + before_each 'cleanup env' => sub { delete $ENV{PERL_MB_OPT}; delete $ENV{PERL_MM_OPT}; delete $ENV{PERL_LOCAL_LIB_ROOT}; @@ -98,4 +97,4 @@ OUT } }; -runtests unless caller; +done_testing; diff --git a/t/command-exec.t b/t/command-exec.t index 31a180e0..855e1b7d 100644 --- a/t/command-exec.t +++ b/t/command-exec.t @@ -1,12 +1,12 @@ #!/usr/bin/env perl -use strict; -use warnings; +use Test2::V0; +use Test2::Tools::Spec; + use FindBin; use lib $FindBin::Bin; use App::perlbrew; -require 'test_helpers.pl'; +require 'test2_helpers.pl'; -use Test::Spec; use Test::Output; mock_perlbrew_install("perl-5.12.3"); @@ -16,261 +16,365 @@ mock_perlbrew_install("perl-5.14.2"); describe 'perlbrew exec perl -E "say 42"' => sub { it "invokes all perls" => sub { - my $app = App::perlbrew->new(qw(exec perl -E), "say 42"); + mocked( + App::perlbrew->new(qw(exec perl -E), "say 42"), + sub { + my ($mock, $app) = @_; - my @perls = $app->installed_perls; + my @perls = $app->installed_perls; - $app->expects("do_system_with_exit_code")->exactly(4)->returns( - sub { - my ($self, @args) = @_; + $mock->expects("do_system_with_exit_code")->exactly(4)->returns( + sub { + my ($self, @args) = @_; - is_deeply \@args, ["perl", "-E", "say 42"]; + is \@args, ["perl", "-E", "say 42"], "arguments"; - my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); + my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); - my $perl_installation = shift @perls; + my $perl_installation = shift @perls; - is $perlbrew_bin_path, App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "bin"); - is $perlbrew_perl_bin_path, App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", $perl_installation->{name}, "bin"), "perls/". $perl_installation->{name} . "/bin"; + is $perlbrew_bin_path, App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "bin")->stringify(), "perlbrew_bin_path"; + is $perlbrew_perl_bin_path, App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", $perl_installation->{name}, "bin")->stringify(), "perls/". $perl_installation->{name} . "/bin"; - return 0; + return 0; + } + ); + + $app->run; } ); - - $app->run; }; }; describe 'perlbrew exec --with perl-5.12.3 perl -E "say 42"' => sub { it "invokes perl-5.12.3/bin/perl" => sub { - my $app = App::perlbrew->new(qw(exec --with perl-5.12.3 perl -E), "say 42"); - - $app->expects("do_system_with_exit_code")->returns( + mocked( + App::perlbrew->new(qw(exec --with perl-5.12.3 perl -E), "say 42"), sub { - my ($self, @args) = @_; + my ($mock, $app) = @_; - is_deeply \@args, ["perl", "-E", "say 42"]; + $mock->expects("do_system_with_exit_code")->returns( + sub { + my ($self, @args) = @_; - my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); + is \@args, ["perl", "-E", "say 42"]; - is $perlbrew_bin_path, App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "bin"); - is $perlbrew_perl_bin_path, App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin"); + my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); - return 0; + is $perlbrew_bin_path, App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "bin")->stringify; + is $perlbrew_perl_bin_path, App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin")->stringify; + + return 0; + } + ); + + $app->run; } ); - - $app->run; }; }; describe 'perlbrew exec --with perl-5.14.1,perl-5.12.3,perl-5.14.2 perl -E "say 42"' => sub { it "invokes each perl in the specified order" => sub { - my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-5.12.3 perl-5.14.2", qw(perl -E), "say 42"); - - my @perl_paths; - $app->expects("do_system_with_exit_code")->exactly(3)->returns( + mocked( + App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-5.12.3 perl-5.14.2", qw(perl -E), "say 42"), sub { - my ($self, @args) = @_; - my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); - push @perl_paths, $perlbrew_perl_bin_path; - return 0; + my ($mock, $app) = @_; + + my @perl_paths; + + $mock->expects("do_system_with_exit_code")->exactly(3)->returns( + sub { + my ($self, @args) = @_; + my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); + push @perl_paths, $perlbrew_perl_bin_path; + return 0; + } + ); + + $app->run; + + is \@perl_paths, [ + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin")->stringify, + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin")->stringify, + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.2", "bin")->stringify, + ]; } ); - - $app->run; - - is_deeply \@perl_paths, [ - App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin"), - App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin"), - App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.2", "bin"), - ]; }; }; describe 'perlbrew exec --with perl-5.14.1,perl-foobarbaz, ' => sub { it "ignore the unrecognized 'perl-foobarbaz'" => sub { - my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-foobarbaz", qw(perl -E), "say 42"); - - my @perl_paths; - $app->expects("do_system_with_exit_code")->returns( + mocked( + App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-foobarbaz", qw(perl -E), "say 42"), sub { - my ($self, @args) = @_; - my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); - push @perl_paths, $perlbrew_perl_bin_path; - return 0; - } - ); - - $app->run; - - is_deeply \@perl_paths, [ - App::Perlbrew::Path->new ($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin"), - ]; + my ($mock, $app) = @_; + + my @perl_paths; + + $mock->expects("do_system_with_exit_code")->returns( + sub { + my ($self, @args) = @_; + my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); + push @perl_paths, $perlbrew_perl_bin_path; + return 0; + } + ); + + $app->run; + + is \@perl_paths, [ + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin")->stringify(), + ]; + }, + ) }; }; describe 'perlbrew exec --with perl-5.14.1,5.14.1 ' => sub { it "exec 5.14.1 twice, since that is what is specified" => sub { - my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1 5.14.1", qw(perl -E), "say 42"); - - my @perl_paths; - $app->expects("do_system_with_exit_code")->exactly(2)->returns( + mocked( + App::perlbrew->new(qw(exec --with), "perl-5.14.1 5.14.1", qw(perl -E), "say 42"), sub { - my ($self, @args) = @_; - my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); - push @perl_paths, $perlbrew_perl_bin_path; - return 0; - } - ); + my ($mock, $app) = @_; - $app->run; + my @perl_paths; + + $mock->expects("do_system_with_exit_code")->exactly(2)->returns( + sub { + my ($self, @args) = @_; + my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); + push @perl_paths, $perlbrew_perl_bin_path; + return 0; + } + ); - is_deeply \@perl_paths, [ - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin"), - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin"), - ]; + $app->run; + + is \@perl_paths, [ + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin")->stringify, + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin")->stringify, + ]; + } + ) }; }; describe 'exec exit code' => sub { describe "logging" => sub { it "should work" => sub { - my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1", qw(perl -E), "somesub 42"); - $app->expects("format_info_output")->exactly(1)->returns("format_info_output_value\n"); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { - die "simulate exit\n"; - }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); - stderr_is sub { - eval { $app->run; 1; }; - }, <<"OUT"; + mocked( + App::perlbrew->new(qw(exec --with), "perl-5.14.1", qw(perl -E), "somesub 42"), + sub { + my ($mock, $app) = @_; + + $mock->expects("format_info_output")->exactly(1)->returns("format_info_output_value\n"); + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); + + my $mock2 = mocked('App::perlbrew'); + $mock2->expects("do_exit_with_error_code")->exactly(1)->returns(sub { die "simulate exit\n" }); + + stderr_is sub { + eval { $app->run; 1; }; + }, <<"OUT"; Command [perl -E 'somesub 42'] terminated with exit code 7 (\$? = 1792) under the following perl environment: format_info_output_value OUT + + $mock2->verify; + } + ) }; + it "should be quiet if asked" => sub { my $app = App::perlbrew->new(qw(exec --quiet --with), "perl-5.14.1", qw(perl -E), "somesub 42"); - $app->expects("format_info_output")->exactly(0)->returns('should not be called!'); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { - die "simulate exit\n"; - }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(0)->returns('should not be called!'); + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); + + my $mock2 = mocked('App::perlbrew'); + $mock2->expects("do_exit_with_error_code")->exactly(1)->returns(sub { die "simulate exit\n" }); + stderr_is sub { eval { $app->run; 1; }; }, ''; + + $mock->verify; + $mock2->verify; }; + it "should format info output for right perl" => sub { my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1", qw(perl -E), "somesub 42"); - $app->expects("format_info_output")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(sub { my ($self) = @_; is $self->current_env, 'perl-5.14.1'; like $self->installed_perl_executable('perl-5.14.1'), qr/perl-5.14.1/; "format_info_output_value\n"; }); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); + + my $mock2 = mocked('App::perlbrew'); + $mock2->expects("do_exit_with_error_code")->exactly(1)->returns(sub { die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(7<<8); + eval { $app->run; 1; }; + + $mock->verify; + $mock2->verify; }; }; + describe "no halt-on-error" => sub { it "should exit with success code when several perls ran" => sub { - my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-5.14.1", qw(perl -E), "say 42"); - App::perlbrew->expects("do_exit_with_error_code")->never; - $app->expects("do_system_with_exit_code")->exactly(2)->returns(0); - $app->run; + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->never; + + mocked( + App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-5.14.1", qw(perl -E), "say 42"), + sub { + my ($mock, $app) = @_; + $mock->expects("do_system_with_exit_code")->exactly(2)->returns(0); + $app->run; + } + ); + + $mock2->verify; }; + it "should exit with error code " => sub { my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1", qw(perl -E), "say 42"); - $app->expects("format_info_output")->exactly(1)->returns(''); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(''); + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(3<<8); + + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->exactly(1)->returns(sub { my ($self, $code) = @_; is $code, 1; # exit with error, but don't propogate exact failure codes die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(3<<8); + + ok !eval { $app->run; 1; }; is $@, "simulate exit\n"; + + $mock->verify; + $mock2->verify; }; + it "should exit with error code when several perls ran" => sub { my $app = App::perlbrew->new(qw(exec --with), "perl-5.14.1 perl-5.14.1", qw(perl -E), "say 42"); - $app->expects("format_info_output")->exactly(1)->returns(''); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(''); + my $calls = 0; + $mock->expects("do_system_with_exit_code")->exactly(2)->returns(sub { + $calls++; + return 0 if ($calls == 2); # second exec call successed + return 3<<8; # first exec failed + }); + + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->exactly(1)->returns(sub { my ($self, $code) = @_; is $code, 1; # exit with error, but don't propogate exact failure codes die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(sub { - $app->expects("do_system_with_exit_code")->exactly(1)->returns(sub { # make sure second call to exec is made - 0; # second call is success - }); - 3<<8; # first exec failed - }); + ok !eval { $app->run; 1; }; is $@, "simulate exit\n"; + + $mock->verify; + $mock2->verify; }; }; + describe "halt-on-error" => sub { it "should exit with success code " => sub { my $app = App::perlbrew->new(qw(exec --halt-on-error --with), "perl-5.14.1", qw(perl -E), "say 42"); - App::perlbrew->expects("do_exit_with_error_code")->never; - $app->expects("do_system_with_exit_code")->exactly(1)->returns(0); + my $mock = mocked('App::perlbrew')->expects("do_exit_with_error_code")->never; + my $mock2 = mocked($app)->expects("do_system_with_exit_code")->exactly(1)->returns(0); $app->run; + $mock->verify; + $mock2->verify; }; + it "should exit with error code " => sub { my $app = App::perlbrew->new(qw(exec --halt-on-error --with), "perl-5.14.1", qw(perl -E), "say 42"); - $app->expects("format_info_output")->exactly(1)->returns(''); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(''); + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(3<<8); + + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->exactly(1)->returns(sub { my ($self, $code) = @_; is $code, 3; die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(3<<8); + ok !eval { $app->run; 1; }; is $@, "simulate exit\n"; + $mock->verify; + $mock2->verify; }; + it "should exit with code 255 if program terminated with signal or something" => sub { my $app = App::perlbrew->new(qw(exec --halt-on-error --with), "perl-5.14.1", qw(perl -E), "say 42"); - $app->expects("format_info_output")->exactly(1)->returns(''); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(''); + $mock->expects("do_system_with_exit_code")->exactly(1)->returns(-1); + + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->exactly(1)->returns(sub { my ($self, $code) = @_; is $code, 255; die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(-1); + ok !eval { $app->run; 1; }; is $@, "simulate exit\n"; + $mock->verify; + $mock2->verify; }; + it "should exit with error code when several perls ran" => sub { my $app = App::perlbrew->new(qw(exec --halt-on-error --with), "perl-5.14.1 perl-5.14.1", qw(perl -E), "say 42"); - $app->expects("format_info_output")->exactly(1)->returns(''); - App::perlbrew->expects("do_exit_with_error_code")->exactly(1)->returns(sub { + + my $mock = mocked($app); + $mock->expects("format_info_output")->exactly(1)->returns(''); + my $calls = 0; + $mock->expects("do_system_with_exit_code")->exactly(2)->returns(sub { + $calls++; + return 7<<8 if $calls == 2; + return 0; + }); + + my $mock2 = mocked('App::perlbrew')->expects("do_exit_with_error_code")->exactly(1)->returns(sub { my ($self, $code) = @_; is $code, 7; die "simulate exit\n"; }); - $app->expects("do_system_with_exit_code")->exactly(1)->returns(sub { - $app->expects("do_system_with_exit_code")->exactly(1)->returns(sub { - 7<<8; - }); - 0; - }); + ok !eval { $app->run; 1; }; is $@, "simulate exit\n"; + + $mock->verify; + $mock2->verify; }; }; }; describe "minimal perl version" => sub { it "only executes the needed version" => sub { - my @perl_paths; my $app = App::perlbrew->new(qw(exec --min 5.014), qw(perl -E), "say 42"); - $app->expects("do_system_with_exit_code")->exactly(2)->returns(sub { + + my $mock = mocked($app)->expects("do_system_with_exit_code")->exactly(2)->returns(sub { my ($self, @args) = @_; my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); push @perl_paths, $perlbrew_perl_bin_path; @@ -280,19 +384,21 @@ describe "minimal perl version" => sub { $app->run; # Don't care about the order, just the fact all of them were visited - is_deeply [sort @perl_paths], [sort ( - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.2", "bin"), - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin"), + is [sort @perl_paths], [sort ( + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.2", "bin")->stringify(), + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.14.1", "bin")->stringify(), )]; + + $mock->verify; }; }; describe "maximum perl version" => sub { it "only executes the needed version" => sub { - my @perl_paths; my $app = App::perlbrew->new(qw(exec --max 5.014), qw(perl -E), "say 42"); - $app->expects("do_system_with_exit_code")->exactly(2)->returns(sub { + + my $mock = mocked($app)->expects("do_system_with_exit_code")->exactly(2)->returns(sub { my ($self, @args) = @_; my ($perlbrew_bin_path, $perlbrew_perl_bin_path, @paths) = split(":", $ENV{PATH}); push @perl_paths, $perlbrew_perl_bin_path; @@ -302,12 +408,13 @@ describe "maximum perl version" => sub { $app->run; # Don't care about the order, just the fact all of them were visited - is_deeply [sort @perl_paths], [sort ( - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.4", "bin"), - App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin"), + is [sort @perl_paths], [sort ( + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.4", "bin")->stringify(), + App::Perlbrew::Path->new($App::perlbrew::PERLBREW_ROOT, "perls", "perl-5.12.3", "bin")->stringify(), )]; + + $mock->verify; }; }; - -runtests unless caller; +done_testing; diff --git a/t/command-info.t b/t/command-info.t index 6dbd2421..feacd7fa 100644 --- a/t/command-info.t +++ b/t/command-info.t @@ -1,7 +1,6 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::Spec; +use Test2::V0; +use Test2::Tools::Spec; use Test::Output; use File::Spec; use Config; @@ -14,12 +13,15 @@ describe "info command" => sub { my $mock_perl = { mock => 'current_perl' }; my $perl_path = $Config{perlpath}; + my $mock = mock $app, + override => [ + current_perl => sub { $mock_perl }, + current_env => sub { 'perl-5.8.9' }, + installed_perl_executable => sub { $perl_path }, + configure_args => sub { 'config_args_value' }, + system_perl_shebang => sub { die } + ]; - $app->expects("current_perl")->returns($mock_perl)->at_least_once(); - $app->expects("current_env")->returns('perl-5.8.9'); - $app->expects("installed_perl_executable")->with($mock_perl)->returns($perl_path)->at_least_once(); - $app->expects("configure_args")->with($mock_perl)->returns('config_args_value'); - $app->expects("system_perl_shebang")->never; local $ENV{PERLBREW_ROOT} = 'perlbrew_root_value'; local $ENV{PERLBREW_HOME} = 'perlbrew_home_value'; local $ENV{PERLBREW_PATH} = 'perlbrew_path_value'; @@ -48,11 +50,15 @@ OUT it "should display info if under system perl" => sub { my $app = App::perlbrew->new("info"); - $app->expects("current_perl")->returns(''); - $app->expects("current_env")->never; - $app->expects("installed_perl_executable")->never; - $app->expects("configure_args")->never; - $app->expects("system_perl_shebang")->returns("system_perl_shebang_value")->once; + my $mock = mock $app, + override => [ + current_perl => sub { '' }, + current_env => sub { die }, + installed_perl_executable => sub { die }, + configure_args => sub { die }, + system_perl_shebang => sub { "system_perl_shebang_value" }, + ]; + local $ENV{PERLBREW_ROOT} = 'perlbrew_root_value'; local $ENV{PERLBREW_HOME} = 'perlbrew_home_value'; local $ENV{PERLBREW_PATH} = 'perlbrew_path_value'; @@ -84,11 +90,15 @@ OUT my $perl_path = $Config{perlpath}; my $module_name = "Data::Dumper"; - $app->expects("current_perl")->returns($mock_perl)->at_least_once(); - $app->expects("current_env")->returns('perl-5.8.9'); - $app->expects("installed_perl_executable")->with($mock_perl)->returns($perl_path)->at_least_once(); - $app->expects("configure_args")->with($mock_perl)->returns('config_args_value'); - $app->expects("system_perl_shebang")->never; + my $mock = mock $app, + override => [ + "current_perl" => sub { $mock_perl }, + "current_env" => sub { 'perl-5.8.9'}, + "installed_perl_executable" => sub { $perl_path }, + "configure_args" => sub { 'config_args_value' }, + "system_perl_shebang" => sub { die }, + ]; + local $ENV{PERLBREW_ROOT} = 'perlbrew_root_value'; local $ENV{PERLBREW_HOME} = 'perlbrew_home_value'; local $ENV{PERLBREW_PATH} = 'perlbrew_path_value'; @@ -131,11 +141,15 @@ OUT my $perl_path = $Config{perlpath}; my $module_name = "SOME_FAKE_MODULE"; - $app->expects("current_perl")->returns($mock_perl)->at_least_once(); - $app->expects("current_env")->returns('perl-5.8.9'); - $app->expects("installed_perl_executable")->with($mock_perl)->returns($perl_path)->at_least_once(); - $app->expects("configure_args")->with($mock_perl)->returns('config_args_value'); - $app->expects("system_perl_shebang")->never; + my $mock = mock $app, + override => [ + "current_perl" => sub { $mock_perl }, + "current_env" => sub { 'perl-5.8.9' }, + "installed_perl_executable" => sub { $perl_path }, + "configure_args" => sub { 'config_args_value' }, + "system_perl_shebang" => sub { die }, + ]; + local $ENV{PERLBREW_ROOT} = 'perlbrew_root_value'; local $ENV{PERLBREW_HOME} = 'perlbrew_home_value'; local $ENV{PERLBREW_PATH} = 'perlbrew_path_value'; @@ -167,5 +181,4 @@ OUT }; - -runtests unless caller; +done_testing; diff --git a/t/command-install-cpanm.t b/t/command-install-cpanm.t index 274769fa..cb6f3db5 100644 --- a/t/command-install-cpanm.t +++ b/t/command-install-cpanm.t @@ -1,7 +1,6 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::Spec; +use Test2::V0; +use Test2::Tools::Spec; use File::Temp qw( tempdir ); use App::perlbrew; @@ -28,4 +27,4 @@ describe "App::perlbrew->install_cpanm" => sub { }; }; -runtests unless caller; +done_testing; diff --git a/t/command-install-from-archive.t b/t/command-install-from-archive.t index 4991f726..15900fdd 100644 --- a/t/command-install-from-archive.t +++ b/t/command-install-from-archive.t @@ -1,25 +1,15 @@ #!/usr/bin/env perl -use strict; -use warnings; - -use Test::Spec 0.49; # with_deep -use Test::Deep; +use Test2::V0; +use Test2::Tools::Spec; use FindBin; use lib $FindBin::Bin; - use App::perlbrew; - -use Hash::Util; - -require 'test_helpers.pl'; +require 'test2_helpers.pl'; sub arrange_file; -sub arrange_available_perls; -sub arrange_command_line; sub expect_dispatch_via; sub should_install_from_archive; -sub is_path; describe "command install " => sub { should_install_from_archive "with perl source archive" => ( @@ -45,63 +35,49 @@ describe "command install " => sub { dist_version => '5.28.0', installation_name => 'cperl-5.28.0', ); - }; -runtests unless caller; +done_testing; sub should_install_from_archive { my ($title, %params) = @_; - Hash::Util::lock_keys %params, - 'filename', - 'dist_version', - 'installation_name', - ; - - context $title => sub { - my $file; + describe $title => sub { + my %shared; - before each => sub { - $file = arrange_file - name => $params{filename},, + before_each 'arrange file', sub { + my $file = $shared{file} = arrange_file + name => $params{filename}, tempdir => 1, ; - arrange_command_line install => $file; + $shared{app} = App::perlbrew->new(install => "$file"); }; expect_dispatch_via + shared => \%shared, method => 'do_install_archive', - with_args => [ - is_path (methods (basename => $params{filename})) - ]; + with_args => [ object { call 'basename' => $params{filename} } ]; - expect_dispatch_via method => 'do_extract_tarball', - stubs => { do_install_this => '' }, - with_args => [ - is_path (methods (basename => $params{filename})) - ]; + expect_dispatch_via + shared => \%shared, + method => 'do_extract_tarball', + with_args => [ object { call 'basename' => $params{filename} } ], + stubs => { do_install_this => '' }; - expect_dispatch_via method => 'do_install_this', - stubs => { do_extract_tarball => sub { $_[-1]->dirname->child('foo') } }, + expect_dispatch_via + shared => \%shared, + method => 'do_install_this', with_args => [ - is_path (methods (basename => 'foo')), + object { call basename => 'foo' }, $params{dist_version}, $params{installation_name}, - ]; + ], + stubs => { do_extract_tarball => sub { $_[-1]->dirname->child('foo') } }; + }; }; -sub is_path { - my (@tests) = @_; - - all ( - obj_isa ('App::Perlbrew::Path'), - @tests, - ); -} - sub arrange_file { my (%params) = @_; @@ -118,30 +94,28 @@ sub arrange_file { return $file; } -sub arrange_command_line { - my (@command_line) = @_; - - share my %shared; - - # Enforce stringification - $shared{app} = App::perlbrew->new(map "$_", @command_line); -} - sub expect_dispatch_via { my (%params) = @_; - - it "should dispatch via $params{method}()" => sub { - share my %shared; - - App::perlbrew->stubs(%{ $params{stubs} }) - if $params{stubs}; - - my $expectation = App::perlbrew->expects($params{method}); - $expectation = $expectation->with_deep(@{ $params{with_args} }) - if $params{with_args}; - - $shared{app}->run; - - ok $expectation->verify; + tests "should dispatch via $params{method}()" => sub { + my $app = $params{shared}{app}; + my $mock = mocked($app); + + if ($params{stubs}) { + $mock->stubs($params{stubs}); + } + + if ($params{with_args}) { + $mock->expects($params{method}) + ->with(@{ $params{with_args} }) + ->returns(undef); + } else { + $mock->expects($params{method}) + ->returns(undef); + } + + $app->run; + + $mock->verify; + $mock->reset; }; } diff --git a/t/command-install.t b/t/command-install.t index dc13d396..a25d40f8 100644 --- a/t/command-install.t +++ b/t/command-install.t @@ -1,111 +1,86 @@ #!/usr/bin/env perl -use strict; -use warnings; - -use Test::Spec 0.49; # with_deep -use Test::Deep; +use Test2::V0; +use Test2::Tools::Spec; use FindBin; use lib $FindBin::Bin; - use App::perlbrew; - -require 'test_helpers.pl'; - -sub arrange_available_perls; -sub arrange_command_line; -sub expect_dispatch_via; +require 'test2_helpers.pl'; describe "command install" => sub { it "should install exact perl version" => sub { - arrange_command_line install => 'perl-5.12.1'; - - expect_dispatch_via do_install_release => [ 'perl-5.12.1', '5.12.1' ]; + my $app = App::perlbrew->new(install => 'perl-5.12.1'); + my $mock = mocked($app)->expects('do_install_release')->with('perl-5.12.1', '5.12.1'); + $app->run; + $mock->verify; }; it "should install exact cperl version" => sub { - arrange_command_line install => 'cperl-5.26.4'; - - expect_dispatch_via do_install_release => [ 'cperl-5.26.4', '5.26.4' ]; + my $app = App::perlbrew->new(install => 'cperl-5.26.4'); + my $mock = mocked($app)->expects('do_install_release')->with('cperl-5.26.4', '5.26.4'); + $app->run; + $mock->verify; }; it "should install stable version of perl" => sub { - arrange_command_line install => 'perl-stable'; + my @versions_sorted_from_new_to_old = qw( perl-5.29.0 perl-5.14.2 perl-5.14.1 perl-5.12.3 perl-5.12.2 ); - arrange_available_perls qw[ - perl-5.12.2 - perl-5.12.3 - perl-5.14.1 - perl-5.14.2 - perl-5.29.0 - ]; + my $app = App::perlbrew->new(install => 'perl-stable'); - expect_dispatch_via do_install_release => [ 'perl-5.14.2', '5.14.2' ]; + my $mock = mocked($app); + $mock->expects('available_perls')->returns(sub { @versions_sorted_from_new_to_old }); + $mock->expects('do_install_release')->with('perl-5.14.2', '5.14.2'); + + $app->run; + $mock->verify; }; it "should install blead perl" => sub { - arrange_command_line install => 'perl-blead'; - expect_dispatch_via do_install_blead => []; + my $app = App::perlbrew->new(install => 'perl-blead'); + + my $mock = mocked($app); + $mock->expects('do_install_release')->never; + $mock->expects('do_install_blead')->exactly(1)->returns(sub { "dummy" }); + + $app->run; + $mock->verify; }; it "should install git checkout" => sub { my $checkout = tempdir (CLEANUP => 1); dir ($checkout, '.git')->mkpath; - arrange_command_line install => $checkout; + my $app = App::perlbrew->new(install => $checkout); - expect_dispatch_via do_install_git => [ $checkout ]; + my $mock = mocked($app); + $mock->expects('do_install_git')->with($checkout)->returns(sub { "dummy" }); + + $app->run; + $mock->verify; }; it "should install from archive" => sub { my $checkout = tempdir (CLEANUP => 1); my $file = file ($checkout, 'archive.tar.gz')->stringify; - open my $fh, '>', $file; close $fh; - arrange_command_line install => $file; + my $app = App::perlbrew->new(install => $file); + + my $mock = mocked($app)->expects('do_install_archive')->with($file)->returns(sub { "dummy" }); - expect_dispatch_via do_install_archive => [ all ( - obj_isa ('App::Perlbrew::Path'), - methods (stringify => $file), - ) ]; + $app->run; + $mock->verify; }; it "should install from uri" => sub { - arrange_command_line install => 'http://example.com/foo/bar'; + my $app = App::perlbrew->new(install => 'http://example.com/foo/bar'); - expect_dispatch_via do_install_url => [ 'http://example.com/foo/bar' ]; + my $mock = mocked($app)->expects('do_install_url')->with('http://example.com/foo/bar')->returns(sub { "dummy" }); + + $app->run; + $mock->verify; }; }; -runtests unless caller; - -sub arrange_available_perls { - my (@list) = @_; - - App::perlbrew->stubs (available_perls => sub { $_[0]->sort_perl_versions (@list) }); -} - -sub arrange_command_line { - my (@command_line) = @_; - - share my %shared; - - $shared{app} = App::perlbrew->new (@command_line); -} - -sub expect_dispatch_via { - my ($method, $arguments) = @_; - - share my %shared; - - my $expectation = App::perlbrew->expects ($method); - $expectation = $expectation->with_deep (@$arguments) - if $arguments; - - - $shared{app}->run; - - ok $expectation->verify; -} +done_testing; diff --git a/t/current_perl.t b/t/current_perl.t index 7b6a0b96..9713abf1 100644 --- a/t/current_perl.t +++ b/t/current_perl.t @@ -1,12 +1,11 @@ #!/usr/bin/env perl -use strict; -use warnings; -use FindBin; -use lib $FindBin::Bin; +use Test2::V0; +use Test2::Tools::Spec; use App::perlbrew; -require 'test_helpers.pl'; -use Test::Spec; +use FindBin; +use lib $FindBin::Bin; +require 'test2_helpers.pl'; mock_perlbrew_install("perl-5.12.3"); mock_perlbrew_install("perl-5.12.4"); @@ -45,5 +44,4 @@ describe "current perl" => sub { }; }; -runtests unless caller; - +done_testing; diff --git a/t/failure-command-install-patchperl.t b/t/failure-command-install-patchperl.t index f475aa0a..88f4f81e 100644 --- a/t/failure-command-install-patchperl.t +++ b/t/failure-command-install-patchperl.t @@ -1,7 +1,6 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::Spec; +use Test2::V0; +use Test2::Tools::Spec; use Path::Class; use File::Temp qw( tempdir ); @@ -37,4 +36,4 @@ describe "App::perlbrew->install_patchperl" => sub { }; }; -runtests unless caller; +done_testing; diff --git a/t/test2_helpers.pl b/t/test2_helpers.pl index 237ddb9b..8d41ff69 100644 --- a/t/test2_helpers.pl +++ b/t/test2_helpers.pl @@ -100,4 +100,162 @@ sub mock_perlbrew_lib_create { App::Perlbrew::Path->new($App::perlbrew::PERLBREW_HOME, "libs", $name)->mkpath; } +# Wrappers around Test2::Tools::Mock, a replacement of Test::Spec, more or less. + +sub mocked { + my ($object, $cb) = @_; + my $mocked = Mocked->new($object); + + if (defined($cb)) { + $cb->($mocked, $object); + $mocked->verify; + } else { + return $mocked; + } +} + +package Mocked; { + use Test2::Tools::Mock qw(mock); + + sub new { + my ($class, $object) = @_; + return bless { + object => $object, + methods => [], + mock => mock($object), + }, $class + } + + sub stubs { + my ($self, $stubs) = @_; + for my $k (keys %$stubs) { + my $v = $stubs->{$k}; + if (ref($v) eq 'CODE') { + $self->{mock}->override($k => $v); + } else { + $self->{mock}->override($k => sub { $v }); + } + } + } + + sub expects { + my ($self, $method) = @_; + my $mockedMethod = MockedMethod->new($self, $method); + push @{$self->{methods}}, $mockedMethod; + return $mockedMethod; + } + + sub verify { + my ($self) = @_; + for (@{$self->{methods}}) { + $_->verify(); + } + } + + sub reset { + my ($self) = @_; + $self->{mock}->reset_all; + $self->{methods} = []; + } +} + +package MockedMethod; { + use Test2::Tools::Basic qw(ok note); + use Test2::Tools::Compare qw(is); + use Test2::Tools::Mock qw(mock); + + sub new { + my ($class, $mocked, $method) = @_; + my $self = bless { + called => 0, + with => undef, + called_with => undef, + at_least => undef, + exactly => undef, + method => $method, + call_through => 1, + returns => undef, + mocked => $mocked, + }, $class; + $mocked->{mock}->override($method => $self->_build_override_method()); + return $self; + } + + sub never { + my ($self) = @_; + $self->exactly(0); + return $self; + } + + sub exactly { + my ($self, $times) = @_; + unless ( defined $times ) { + die "`exactly` requires a numerical argument."; + } + $self->{exactly} = $times; + $self->{at_least} = undef; + return $self; + } + + sub at_least { + my ($self, $times) = @_; + unless ( defined $times ) { + die "`exactly` requires a numerical argument."; + } + $self->{exactly} = undef; + $self->{at_least} = $times; + return $self; + } + + sub _build_override_method { + my ($self) = @_; + return sub { + my $object = shift; + $self->{called_with} = \@_; + $self->{called}++; + + if ($self->{call_through}) { + my $method = $self->{mocked}{mock}->orig($self->{method}); + $object->$method(@_); + } else { + my $cb_or_value = $self->{returns}; + (ref($cb_or_value) eq 'CODE') ? $cb_or_value->($object, @_) : $cb_or_value; + } + } + } + + sub returns { + my ($self, $cb_or_value) = @_; + $self->{call_through} = 0; + $self->{returns} = $cb_or_value; + return $self; + } + + sub with { + my ($self, @args) = @_; + $self->{with} = \@args; + return $self; + } + + sub verify { + my ($self) = @_; + + if (defined $self->{with}) { + is $self->{called_with}, $self->{with}, "method " . $self->{method} . " is called with expected arguments"; + } + + if (defined $self->{exactly}) { + is $self->{called}, $self->{exactly}, $self->{method} . " should be called exactly " . $self->{exactly} . " times"; + } + elsif (defined $self->{at_least}) { + ok $self->{called} > $self->{at_least}, $self->{method} . " is called at least " . $self->{at_least} . " time"; + } + else { + ok $self->{called} > 0, $self->{method} . " is called at least 1 time"; + } + + return $self; + } +} + 1;