diff options
Diffstat (limited to 't/proxy-with-stack-trace.t')
-rw-r--r-- | t/proxy-with-stack-trace.t | 474 |
1 files changed, 390 insertions, 84 deletions
diff --git a/t/proxy-with-stack-trace.t b/t/proxy-with-stack-trace.t index 3f69917..4d49b63 100644 --- a/t/proxy-with-stack-trace.t +++ b/t/proxy-with-stack-trace.t @@ -2,138 +2,444 @@ use strict; use warnings; use Test::More; +use List::Util qw( any ); use Log::Any; - -plan tests => 48; +use Scalar::Util qw( blessed ); +use Storable qw( dclone ); use FindBin; use lib $FindBin::RealBin; use TestAdapters; +my ( + $num_tests, + $have_Mojo_Exception, + $have_Moose_Exception, + $have_Throwable_Error, +); BEGIN { + + $num_tests = 152; + eval { + require Mojo::Exception; + $have_Mojo_Exception = 1; + $num_tests += 27; + }; + eval { + require Throwable::Error; + $have_Throwable_Error = 1; + $num_tests += 31; + }; + eval { + require Moose::Exception; + $have_Moose_Exception = 1; + $num_tests += 31; + }; + eval { require Devel::StackTrace; Devel::StackTrace->VERSION( 2.00 ); }; if ( $@ ) { plan skip_all => 'Devel::StackTrace >= 2.00 is required for this test'; - } - else { - eval { - require Storable; - Storable->VERSION( 3.08 ); - }; - if ( $@ ) { - plan skip_all => 'Storable >= 3.08 is required for this test'; - } + $num_tests = 0; } } +plan tests => $num_tests if $num_tests; + use Log::Any::Proxy::WithStackTrace; # necessary? -my $default_log = Log::Any->get_logger; -my $log = Log::Any->get_logger( proxy_class => 'WithStackTrace' ); +my $default_log = Log::Any->get_logger; +my $log = Log::Any->get_logger( proxy_class => 'WithStackTrace' ); +my $log_show_args = Log::Any->get_logger( proxy_class => 'WithStackTrace', proxy_show_stack_trace_args => 1); -is ref $default_log, 'Log::Any::Proxy::Null', +is ref $default_log, 'Log::Any::Proxy::Null', 'no adapter default proxy is Null'; -is ref $log, 'Log::Any::Proxy::WithStackTrace', +is ref $log, 'Log::Any::Proxy::WithStackTrace', 'no adapter explicit proxy is WithStackTrace'; +is ref $log_show_args, 'Log::Any::Proxy::WithStackTrace', + 'no adapter explicit proxy with proxy_show_stack_trace_args flag is WithStackTrace'; -$default_log->info("test"); -$log ->info("test"); +$default_log ->info("test"); +$log ->info("test"); +$log_show_args->info("test"); -is ref $default_log, 'Log::Any::Proxy::Null', +is ref $default_log, 'Log::Any::Proxy::Null', 'no adapter default proxy is still Null after logging'; -is ref $log, 'Log::Any::Proxy::WithStackTrace', +is ref $log, 'Log::Any::Proxy::WithStackTrace', 'no adapter explicit proxy is still WithStackTrace after logging'; +is ref $log_show_args, 'Log::Any::Proxy::WithStackTrace', + 'no adapter explicit proxy with proxy_show_stack_trace_args flag is still WithStackTrace after logging'; Log::Any->set_adapter('+TestAdapters::Structured'); -is ref $default_log, 'Log::Any::Proxy', +is ref $default_log, 'Log::Any::Proxy', 'existing default proxy is reblessed after adapter'; -is ref $log, 'Log::Any::Proxy::WithStackTrace', +is !!$default_log->{proxy_show_stack_trace_args}, '', + 'Defauly log does not proxy_show_stack_trace_args'; +is ref $log, 'Log::Any::Proxy::WithStackTrace', 'existing explicit proxy is still WithStackTrace after adapter'; +is !!$log->{proxy_show_stack_trace_args}, '', + 'WithStackTrace does not proxy_show_stack_trace_args'; +is ref $log_show_args, 'Log::Any::Proxy::WithStackTrace', + 'existing explicit proxy with proxy_show_stack_trace_args flag is still WithStackTrace after adapter'; +is !!$log_show_args->{proxy_show_stack_trace_args}, 1, + 'WithStackTrace does proxy_show_stack_trace_args'; -is ref $default_log->adapter, 'TestAdapters::Structured', +is ref $default_log->adapter, 'TestAdapters::Structured', 'existing default proxy has correct adapter'; -is ref $log->adapter, 'TestAdapters::Structured', +is ref $log->adapter, 'TestAdapters::Structured', 'existing explicit proxy has correct adapter'; +is ref $log_show_args->adapter, 'TestAdapters::Structured', + 'existing explicit proxy with proxy_show_stack_trace_args flag has correct adapter'; + +################################################################### + +# Dummy default for initial call: +my $logger = $default_log; +my $message = "dummy"; +my $extra_args = []; + +my ($Mojo_Exception, $Moose_Exception, $Throwable_Error); + +sub foo +{ + sub bar { + + # Log with a stack trace that is 3 frames deep (main->foo->bar): + $logger->info($message, @$extra_args); + + # Create a Mojo::Exception with a similar stack trace: + if ($have_Mojo_Exception && !$Mojo_Exception) { + local $@; + eval { Mojo::Exception->throw("Help!") }; + $Mojo_Exception = $@; + } + + # Create a Moose::Exception with a similar stack trace: + if ($have_Moose_Exception && !$Moose_Exception) { + $Moose_Exception = Moose::Exception->new(message => "Help!"); + } + + # Create a Throwable::Error with a similar stack trace: + if ($have_Throwable_Error && !$Throwable_Error) { + local $@; + eval { Throwable::Error->throw("Help!") }; + $Throwable_Error = $@; + # Default log adapter doesn't like coderefs: + $Throwable_Error->stack_trace->{frame_filter} = undef; + $Throwable_Error->{stack_trace_args} = undef; + } + } + + bar("quux"); +} + +# Make sure exception objects get initialized: +foo("bar", "baz") if $have_Mojo_Exception || + $have_Moose_Exception || + $have_Throwable_Error; -my @test_cases = ( +my ($desc, $expected_by_type); + +foreach my $t ( [ - 'simple', - [ 'test' ], - 'test', + "with string", + "Help!", + [], + { + "default log" => "Help!", + "proxy log" => [ + "Help!", + "Log::Any::MessageWithStackTrace", + ], + "proxy log show args" => [ + "Help!", + "Log::Any::MessageWithStackTrace", + ], + }, ], [ - 'with structured data', - [ 'test', { foo => 1 } ], - 'test', + "with string and extra args", + "Help!", + [ {extra => "data"} ], + { + "default log" => "Help!", + "proxy log" => [ + "Help!", + "Log::Any::MessageWithStackTrace", + ], + "proxy log show args" => [ + "Help!", + "Log::Any::MessageWithStackTrace", + ], + }, ], [ - 'formatted', - [ 'test %s', 'extra' ], - 'test extra', + "with string and bad extra args", + "Help!", + [ {extra => "data"}, "huh?" ], + { + "default log" => "Help!", + # no automatic object inflation if unexpected args: + "proxy log" => "Help!", + "proxy log show args" => "Help!", + }, ], -); - -sub check_test_cases { - foreach my $test_case (@test_cases) { - my ($desc, $args, $expected) = @$test_case; + [ + "with string and bad non-hashref extra args", + "Help!", + [ "huh?" ], + { + "default log" => "Help!", + # no automatic object inflation if unexpected args: + "proxy log" => "Help!", + "proxy log show args" => "Help!", + }, + ], + [ + "with non-string unblessed message", + {foo => "bar"}, + [], + { + "default log" => [ + {foo => "bar"}, + "HASH", + ], + # no automatic object inflation if non-string message: + "proxy log" => [ + {foo => "bar"}, + "HASH", + ], + "proxy log show args" => [ + {foo => "bar"}, + "HASH", + ], + }, + ], + [ + "with dummy blessed object", + bless({foo => "bar"}, "DummyError"), + [], + { + "default log" => [ + qr{^DummyError=HASH\(0x[0-9a-f]+\)}, + "DummyError", + ], + # no automatic object inflation if random blessed message: + "proxy log" => [ + qr{^DummyError=HASH\(0x[0-9a-f]+\)}, + "DummyError", + ], + "proxy log show args" => [ + qr{^DummyError=HASH\(0x[0-9a-f]+\)}, + "DummyError", + ], + }, + ], + [ + "with Mojo::Exception message", + $Mojo_Exception, + [], + { + "default log" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\.}, + "Mojo::Exception", + ], + "proxy log" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\.}, + "Mojo::Exception", + ], + "proxy log show args" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\.}, + "Mojo::Exception", + ], + }, + ], + [ + "with Moose::Exception message", + $Moose_Exception, + [], + { + "default log" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\n}, + "Moose::Exception", + ], + "proxy log" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\n}, + "Moose::Exception", + ], + "proxy log show args" => [ + qr{^Help! at t/proxy-with-stack-trace\.t line \d+\n}, + "Moose::Exception", + ], + }, + ], + [ + "with Throwable::Error message", + $Throwable_Error, + [], + { + "default log" => [ + qr{^Help!\n\nTrace begun at t/proxy-with-stack-trace\.t line \d+\n}, + "Throwable::Error", + ], + "proxy log" => [ + qr{^Help!\n\nTrace begun at t/proxy-with-stack-trace\.t line \d+\n}, + "Throwable::Error", + ], + "proxy log show args" => [ + qr{^Help!\n\nTrace begun at t/proxy-with-stack-trace\.t line \d+\n}, + "Throwable::Error", + ], + }, + ], +) { + my $orig_message; + ($desc, $orig_message, $extra_args, $expected_by_type) = @$t; - my $is_formatted = $args->[0] =~ /%/; + # This can happen if one of the optional exception modules is not + # loaded: + next unless $orig_message; - my $method = $is_formatted ? 'infof' : 'info'; + foreach my $type (sort keys %$expected_by_type) { - my ($msgs, $msg); + $message = ref $orig_message ? dclone $orig_message : $orig_message; - my $type = 'default'; + $logger = { + "default log" => $default_log, + "proxy log" => $log, + "proxy log show args" => $log_show_args, + }->{$type}; @TestAdapters::STRUCTURED_LOG = (); - $default_log->$method(@$args); - $msgs = \@TestAdapters::STRUCTURED_LOG; - is @$msgs, 1, "$desc expected number of structured messages from $type logger"; - is $msgs->[0]->{category}, 'main', - "$desc expected category from $type logger"; - is $msgs->[0]->{level}, 'info', - "$desc expected level from $type logger"; - $msg = $msgs->[0]->{messages}->[0]; # "messages" for text - is $msg, $expected, - "$desc expected message from $type logger"; - - $type = 'stack trace'; + foo("bar", "baz"); - @TestAdapters::STRUCTURED_LOG = (); - $log->$method(@$args); - $msgs = \@TestAdapters::STRUCTURED_LOG; - is @$msgs, 1, "$desc expected number of structured messages from $type logger"; - is $msgs->[0]->{category}, 'main', - "$desc expected category from $type logger"; - is $msgs->[0]->{level}, 'info', - "$desc expected level from $type logger"; - $msg = $msgs->[0]->{data}->[0]; # "data" for non-text - is ref $msg, 'Log::Any::MessageWithStackTrace', - "$desc expected message object from $type logger"; - is "$msg", $expected, - "$desc expected stringified message from $type logger"; - my $trace = $msg->stack_trace; - is ref $trace, 'Devel::StackTrace', - "$desc expected stack_trace object from $type logger"; - is $trace->frame_count, 2, - "$desc stack_trace object has expected number of frames from $type logger"; - # first frame should be the call to "info" inside this sub (19 lines up), - # second frame should be the call to this sub from main - is $trace->frame(0)->line, __LINE__ - 19, - "$desc stack_trace object has expected first frame from $type logger"; - is $trace->frame(1)->subroutine, 'main::check_test_cases', - "$desc stack_trace object has expected second frame from $type logger"; - if (!$is_formatted && @$args > 1) { - my $more_data = $msgs->[0]->{data}->[1]; - is_deeply $more_data, $args->[1], - "expected structured data from $type logger"; + my $logged = \@TestAdapters::STRUCTURED_LOG; + + my $long_desc = "$type $desc"; + + is @$logged, 1, + "$long_desc - got expected number of log messages"; + my $msg = $logged->[0]; + is $msg->{category}, 'main', + "$long_desc - got expected category"; + is $msg->{level}, 'info', + "$long_desc - got expected level"; + + my $expected = $expected_by_type->{$type}; + + if (ref $expected) { + my $messages = $msg->{messages}; + is $messages, undef, + "$long_desc - got expected number of structured messages"; + my $data = $msg->{data}; + if (@$extra_args == 0) { + is @$data, 1, + "$long_desc - got expected number of structured data"; + } + elsif (@$extra_args == 1 && ref $extra_args->[0] eq 'HASH') { + is @$data, 2, + "$long_desc - got expected number of structured data"; + is_deeply $data->[1], $extra_args->[0], + "$long_desc - got expected extra structured data"; + } + else { + is $data, undef, + "$long_desc - got expected number of structured data"; + } + my $thing = $data->[0]; + my $blessed = blessed $thing; + + my $expected_value = $expected->[0]; + my $expected_type = $expected->[1]; + + if ($blessed || ! ref $expected_value) { + + if (ref $expected_value eq 'Regexp') { + like "$thing", $expected_value, + "$long_desc - message stringifies correctly"; + } + else { + is "$thing", $expected_value, + "$long_desc - message stringifies correctly"; + } + } + is ref $thing, $expected_type, + "$long_desc - expected type of structured data got logged"; + + my (@frames, $stack_trace); + if ($blessed) { + @frames = $thing->can("frames") ? $thing->frames : (); + unless (@frames) { + $stack_trace = $thing->can("stack_trace") + ? $thing->stack_trace + : $thing->can("trace") + ? $thing->trace : undef; + @frames = $stack_trace->frames if $stack_trace; + } + } + if (@frames) { + + # Mojo::Exception returns a listref istead of a list: + @frames = @{$frames[0]} if @frames == 1 && + ref $frames[0] eq 'ARRAY'; + + my $frame = $frames[-1]; + my $sub = $expected_type eq "Mojo::Exception" + ? $frame->[3] : $frame->subroutine; + is $sub, "main::foo", + "$long_desc - first frame has correct sub"; + unless ($expected_type eq "Mojo::Exception") { + if ($type eq "proxy log") { + is_deeply [$frame->args], [], + "$long_desc - first frame has expected args"; + } + elsif ($type eq "proxy log show args") { + is_deeply [$frame->args], ["bar","baz"], + "$long_desc - first frame has expected args"; + } + } + $frame = $frames[-2]; + $sub = $expected_type eq "Mojo::Exception" + ? $frame->[3] : $frame->subroutine; + is $sub, "main::bar", + "$long_desc - second frame has correct sub"; + unless ($expected_type eq "Mojo::Exception") { + if ($type eq "proxy log") { + is_deeply [$frame->args], [], + "$long_desc - second frame has expected args"; + } + elsif ($type eq "proxy log show args") { + is_deeply [$frame->args], ["quux"], + "$long_desc - second frame has expected args"; + } + } + } + } + else { + my $messages = $msg->{messages}; + my @expected = ($expected); + push @expected, $extra_args->[0] if $extra_args->[0] && + ref $extra_args->[0] ne 'HASH'; + push @expected, $extra_args->[1] if $extra_args->[1]; + is @$messages, @expected, + "$long_desc - got expected number of structured messages"; + is_deeply $messages, \@expected, + "$long_desc - expected structured message got logged"; + my $data = $msg->{data}; + if (ref $extra_args->[0] eq 'HASH') { + is @$data, 1, + "$long_desc - got expected number of structured data"; + is_deeply $data->[0], $extra_args->[0], + "$long_desc - got expected structured data"; + } + else { + is $data, undef, + "$long_desc - got expected number of structured data"; + } } } } -check_test_cases(); - |