1## no critic (Modules::ProhibitExcessMainComplexity)
2use strict;
3use warnings;
4
5use Test::More 0.96;
6use Test::Fatal;
7
8use File::Spec;
9use File::Temp qw( tempdir );
10use Module::Runtime qw( use_module );
11use Try::Tiny;
12
13use Log::Dispatch;
14
15my %tests;
16
17BEGIN {
18    local $@ = undef;
19    for (qw( MailSend MIMELite MailSendmail MailSender )) {
20        ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval)
21        eval "use Log::Dispatch::Email::$_";
22        $tests{$_} = !$@;
23        $tests{$_} = 0 if $ENV{LD_NO_MAIL};
24    }
25}
26
27my %TestConfig;
28if ( my $email_address = $ENV{LOG_DISPATCH_TEST_EMAIL} ) {
29    %TestConfig = ( email_address => $email_address );
30}
31
32my @syswrite_strs;
33
34BEGIN {
35    if ( $] >= 5.016 ) {
36        my $syswrite = \&CORE::syswrite;
37        *CORE::GLOBAL::syswrite = sub {
38            my ( $fh, $str, @other ) = @_;
39            push @syswrite_strs, $_[1];
40
41            return $syswrite->( $fh, $str, @other );
42        };
43    }
44}
45
46use Log::Dispatch::File;
47use Log::Dispatch::Handle;
48use Log::Dispatch::Null;
49use Log::Dispatch::Screen;
50
51use IO::File;
52
53my $tempdir = tempdir( CLEANUP => 1 );
54
55subtest(
56    'Test Log::Dispatch::File',
57    sub {
58        my $dispatch = Log::Dispatch->new;
59        ok( $dispatch, 'created Log::Dispatch object' );
60
61        my $emerg_log = File::Spec->catdir( $tempdir, 'emerg.log' );
62
63        $dispatch->add(
64            Log::Dispatch::File->new(
65                name      => 'file1',
66                min_level => 'emerg',
67                filename  => $emerg_log
68            )
69        );
70
71        $dispatch->log( level => 'info',  message => "info level 1\n" );
72        $dispatch->log( level => 'emerg', message => "emerg level 1\n" );
73
74        my $debug_log = File::Spec->catdir( $tempdir, 'debug.log' );
75
76        $dispatch->add(
77            Log::Dispatch::File->new(
78                name      => 'file2',
79                min_level => 'debug',
80                syswrite  => 1,
81                filename  => $debug_log
82            )
83        );
84
85        my %outputs = map { $_->name() => ref $_ } $dispatch->outputs();
86        is_deeply(
87            \%outputs, {
88                file1 => 'Log::Dispatch::File',
89                file2 => 'Log::Dispatch::File',
90            },
91            '->outputs() method returns all output objects'
92        );
93
94        $dispatch->log( level => 'info',  message => "info level 2\n" );
95        $dispatch->log( level => 'emerg', message => "emerg level 2\n" );
96
97        # This'll close them filehandles!
98        undef $dispatch;
99
100        ## no critic (InputOutput::RequireBriefOpen)
101        open my $emerg_fh, '<', $emerg_log
102            or die "Can't read $emerg_log: $!";
103        open my $debug_fh, '<', $debug_log
104            or die "Can't read $debug_log: $!";
105
106        my @log = <$emerg_fh>;
107        is(
108            $log[0], "emerg level 1\n",
109            q{First line in log file set to level 'emerg' is 'emerg level 1'}
110        );
111
112        is(
113            $log[1], "emerg level 2\n",
114            q{Second line in log file set to level 'emerg' is 'emerg level 2'}
115        );
116
117        @log = <$debug_fh>;
118        is(
119            $log[0], "info level 2\n",
120            q{First line in log file set to level 'debug' is 'info level 2'}
121        );
122
123        is(
124            $log[1], "emerg level 2\n",
125            q{Second line in log file set to level 'debug' is 'emerg level 2'}
126        );
127
128        close $emerg_fh or die $!;
129        close $debug_fh or die $!;
130
131    SKIP:
132        {
133            ## no critic (ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions)
134            skip 'This test requires Perl 5.16+', 1
135                unless $] >= 5.016;
136            is_deeply(
137                \@syswrite_strs,
138                [
139                    "info level 2\n",
140                    "emerg level 2\n",
141                ],
142                'second LD object used syswrite',
143            );
144        }
145    }
146);
147
148subtest(
149    'max_level',
150    sub {
151        my $max_log = File::Spec->catfile( $tempdir, 'max.log' );
152
153        my $dispatch = Log::Dispatch->new;
154        $dispatch->add(
155            Log::Dispatch::File->new(
156                name      => 'file1',
157                min_level => 'debug',
158                max_level => 'crit',
159                filename  => $max_log
160            )
161        );
162
163        $dispatch->log( level => 'emerg', message => "emergency\n" );
164        $dispatch->log( level => 'crit',  message => "critical\n" );
165
166        undef $dispatch;    # close file handles
167
168        open my $fh, '<', $max_log
169            or die "Can't read $max_log: $!";
170        my @log = <$fh>;
171        close $fh or die $!;
172
173        is(
174            $log[0], "critical\n",
175            q{First line in log file with a max level of 'crit' is 'critical'}
176        );
177    }
178);
179
180subtest(
181    'Handle output',
182    sub {
183        my $handle_log = File::Spec->catfile( $tempdir, 'handle.log' );
184
185        my $fh = IO::File->new( $handle_log, 'w' )
186            or die "Can't write to $handle_log: $!";
187
188        my $dispatch = Log::Dispatch->new;
189        $dispatch->add(
190            Log::Dispatch::Handle->new(
191                name      => 'handle',
192                min_level => 'debug',
193                handle    => $fh
194            )
195        );
196
197        $dispatch->log( level => 'notice', message => "handle test\n" );
198
199        # close file handles
200        undef $dispatch;
201        undef $fh;
202
203        open $fh, '<', $handle_log
204            or die "Can't open $handle_log: $!";
205
206        my @log = <$fh>;
207
208        close $fh or die $!;
209
210        is(
211            $log[0], "handle test\n",
212            q{Log::Dispatch::Handle created log file should contain 'handle test\\n'}
213        );
214    }
215);
216
217subtest(
218    'Email::MailSend output',
219    sub {
220    SKIP:
221        {
222            skip 'Cannot do MailSend tests', 1
223                unless $tests{MailSend} && $TestConfig{email_address};
224
225            my $dispatch = Log::Dispatch->new;
226
227            $dispatch->add(
228                Log::Dispatch::Email::MailSend->new(
229                    name      => 'Mail::Send',
230                    min_level => 'debug',
231                    to        => $TestConfig{email_address},
232                    subject   => 'Log::Dispatch test suite'
233                )
234            );
235
236            $dispatch->log(
237                level => 'emerg',
238                message =>
239                    "Mail::Send test - If you can read this then the test succeeded (PID $$)"
240            );
241
242            diag(
243                "Sending email with Mail::Send to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
244            );
245            undef $dispatch;
246
247            ok( 1, 'sent email via MailSend' );
248        }
249    }
250);
251
252subtest(
253    'Email::MailSendmail output',
254    sub {
255    SKIP:
256        {
257            skip 'Cannot do MailSendmail tests', 1
258                unless $tests{MailSendmail} && $TestConfig{email_address};
259
260            my $dispatch = Log::Dispatch->new;
261
262            $dispatch->add(
263                Log::Dispatch::Email::MailSendmail->new(
264                    name      => 'Mail::Sendmail',
265                    min_level => 'debug',
266                    to        => $TestConfig{email_address},
267                    subject   => 'Log::Dispatch test suite'
268                )
269            );
270
271            $dispatch->log(
272                level => 'emerg',
273                message =>
274                    "Mail::Sendmail test - If you can read this then the test succeeded (PID $$)"
275            );
276
277            diag(
278                "Sending email with Mail::Sendmail to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
279            );
280            undef $dispatch;
281
282            ok( 1, 'sent email via MailSendmail' );
283        }
284    }
285);
286
287subtest(
288    'Email::MIMELite output',
289    sub {
290    SKIP:
291        {
292
293            skip 'Cannot do MIMELite tests', 1
294                unless $tests{MIMELite} && $TestConfig{email_address};
295
296            my $dispatch = Log::Dispatch->new;
297
298            $dispatch->add(
299                Log::Dispatch::Email::MIMELite->new(
300                    name      => 'Mime::Lite',
301                    min_level => 'debug',
302                    to        => $TestConfig{email_address},
303                    subject   => 'Log::Dispatch test suite'
304                )
305            );
306
307            $dispatch->log(
308                level => 'emerg',
309                message =>
310                    "MIME::Lite - If you can read this then the test succeeded (PID $$)"
311            );
312
313            diag(
314                "Sending email with MIME::Lite to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
315            );
316            undef $dispatch;
317
318            ok( 1, 'sent mail via MIMELite' );
319        }
320    }
321);
322
323subtest(
324    'Email::MailSender output',
325    sub {
326    SKIP:
327        {
328            skip 'Cannot do MailSender tests', 1
329                unless $tests{MailSender} && $TestConfig{email_address};
330
331            my $dispatch = Log::Dispatch->new;
332
333            $dispatch->add(
334                Log::Dispatch::Email::MailSender->new(
335                    name      => 'Mail::Sender',
336                    min_level => 'debug',
337                    smtp      => 'localhost',
338                    to        => $TestConfig{email_address},
339                    subject   => 'Log::Dispatch test suite'
340                )
341            );
342
343            $dispatch->log(
344                level => 'emerg',
345                message =>
346                    "Mail::Sender - If you can read this then the test succeeded (PID $$)"
347            );
348
349            diag(
350                "Sending email with Mail::Sender to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
351            );
352            undef $dispatch;
353
354            ok( 1, 'sent email via MailSender' );
355        }
356    }
357);
358
359subtest(
360    'Log::Dispatch::Output->accepted_levels',
361    sub {
362        my $l = Log::Dispatch::Screen->new(
363            name      => 'foo',
364            min_level => 'warning',
365            max_level => 'alert',
366            stderr    => 0
367        );
368
369        my @expected = qw(warning error critical alert);
370        my @levels   = $l->accepted_levels;
371
372        is_deeply(
373            \@expected,
374            \@levels,
375            'accepted_levels matches what is expected'
376        );
377    }
378);
379
380subtest(
381    'Log::Dispatch single callback',
382    sub {
383        my $reverse  = sub { my %p = @_; return reverse $p{message}; };
384        my $dispatch = Log::Dispatch->new( callbacks => $reverse );
385
386        my $string;
387        $dispatch->add(
388            Log::Dispatch::String->new(
389                name      => 'foo',
390                string    => \$string,
391                min_level => 'warning',
392                max_level => 'alert',
393            )
394        );
395
396        $dispatch->log( level => 'warning', message => 'esrever' );
397
398        is(
399            $string, 'reverse',
400            'callback to reverse text'
401        );
402    }
403);
404
405subtest(
406    'Log::Dispatch multiple callbacks',
407    sub {
408        my $reverse = sub { my %p = @_; return reverse $p{message}; };
409        my $uc      = sub { my %p = @_; return uc $p{message}; };
410
411        my $dispatch = Log::Dispatch->new( callbacks => [ $reverse, $uc ] );
412
413        my $string;
414        $dispatch->add(
415            Log::Dispatch::String->new(
416                name      => 'foo',
417                string    => \$string,
418                min_level => 'warning',
419                max_level => 'alert',
420            )
421        );
422
423        $dispatch->log( level => 'warning', message => 'esrever' );
424
425        is(
426            $string, 'REVERSE',
427            'callback to reverse and uppercase text'
428        );
429
430        is_deeply(
431            [ $dispatch->callbacks() ],
432            [ $reverse, $uc ],
433            '->callbacks() method returns all of the callback subs'
434        );
435
436        my $clone = $dispatch->clone();
437        is_deeply(
438            $clone,
439            $dispatch,
440            'clone is a shallow clone of the original object'
441        );
442
443        $clone->add(
444            Log::Dispatch::Screen->new(
445                name      => 'screen',
446                min_level => 'debug',
447            )
448        );
449        my @orig_outputs  = map { $_->name() } $dispatch->outputs();
450        my @clone_outputs = map { $_->name() } $clone->outputs();
451        isnt(
452            scalar(@orig_outputs),
453            scalar(@clone_outputs),
454            'clone is not the same as original after adding an output'
455        );
456
457        $clone->add_callback( sub { return 'foo' } );
458        my @orig_cb  = $dispatch->callbacks();
459        my @clone_cb = $clone->callbacks();
460        isnt(
461            scalar(@orig_cb),
462            scalar(@clone_cb),
463            'clone is not the same as original after adding a callback'
464        );
465    }
466);
467
468subtest(
469    'Log::Dispatch::Output single callback',
470    sub {
471        my $reverse = sub { my %p = @_; return reverse $p{message}; };
472
473        my $dispatch = Log::Dispatch->new;
474
475        my $string;
476        $dispatch->add(
477            Log::Dispatch::String->new(
478                name      => 'foo',
479                string    => \$string,
480                min_level => 'warning',
481                max_level => 'alert',
482                callbacks => $reverse
483            )
484        );
485
486        $dispatch->log( level => 'warning', message => 'esrever' );
487
488        is(
489            $string, 'reverse',
490            'Log::Dispatch::Output callback to reverse text'
491        );
492    }
493);
494
495subtest(
496    'Log::Dispatch::Output multiple callbacks',
497    sub {
498        my $reverse = sub { my %p = @_; return reverse $p{message}; };
499        my $uc      = sub { my %p = @_; return uc $p{message}; };
500
501        my $dispatch = Log::Dispatch->new;
502
503        my $string;
504        $dispatch->add(
505            Log::Dispatch::String->new(
506                name      => 'foo',
507                string    => \$string,
508                min_level => 'warning',
509                max_level => 'alert',
510                callbacks => [ $reverse, $uc ]
511            )
512        );
513
514        $dispatch->log( level => 'warning', message => 'esrever' );
515
516        is(
517            $string, 'REVERSE',
518            'Log::Dispatch::Output callbacks to reverse and uppercase text'
519        );
520    }
521);
522
523subtest(
524    'level parameter to callbacks',
525    sub {
526        my $level = sub { my %p = @_; return uc $p{level}; };
527
528        my $dispatch = Log::Dispatch->new( callbacks => $level );
529
530        my $string;
531        $dispatch->add(
532            Log::Dispatch::String->new(
533                name      => 'foo',
534                string    => \$string,
535                min_level => 'warning',
536                max_level => 'alert',
537                stderr    => 0
538            )
539        );
540
541        $dispatch->log( level => 'warning', message => 'esrever' );
542
543        is(
544            $string, 'WARNING',
545            'Log::Dispatch callback to uppercase the level parameter'
546        );
547    }
548);
549
550subtest(
551    'level name methods',
552    sub {
553        my %levels = map { $_ => $_ }
554            (qw( debug info notice warning error critical alert emergency ));
555        @levels{qw( warn err crit emerg )}
556            = (qw( warning error critical emergency ));
557
558        for my $allowed_level (
559            qw( debug info notice warning error critical alert emergency )) {
560            my $dispatch = Log::Dispatch->new;
561
562            my $string;
563            $dispatch->add(
564                Log::Dispatch::String->new(
565                    name      => 'foo',
566                    string    => \$string,
567                    min_level => $allowed_level,
568                    max_level => $allowed_level,
569                )
570            );
571
572            for my $test_level (
573                qw( debug info notice warn warning err
574                error crit critical alert emerg emergency )
575            ) {
576                $string = q{};
577                $dispatch->$test_level( $test_level, 'test' );
578
579                if ( $levels{$test_level} eq $allowed_level ) {
580                    my $expect = join $", $test_level, 'test';
581                    is(
582                        $string, $expect,
583                        qq{Calling $test_level method should send message '$expect'}
584                    );
585                }
586                else {
587                    ok(
588                        !length $string,
589                        "Calling $test_level method should not log anything"
590                    );
591                }
592            }
593        }
594    }
595);
596
597subtest(
598    'argument variations to name method',
599    sub {
600        my $string;
601        my $dispatch = Log::Dispatch->new(
602            outputs => [
603                [
604                    'String',
605                    name      => 'string',
606                    string    => \$string,
607                    min_level => 'debug',
608                ],
609            ],
610        );
611
612        $dispatch->debug( 'foo', 'bar' );
613        is(
614            $string,
615            'foo bar',
616            'passing multiple elements to ->debug stringifies them like an array'
617        );
618
619        $string = q{};
620        $dispatch->debug( sub {'foo'} );
621        is(
622            $string,
623            'foo',
624            'passing single sub ref to ->debug calls the sub ref'
625        );
626
627    }
628);
629
630subtest(
631    'Log::Dispatch->level_is_valid method',
632    sub {
633        for my $l (
634            qw( debug info notice warning err error
635            crit critical alert emerg emergency )
636        ) {
637            ok( Log::Dispatch->level_is_valid($l), "$l is valid level" );
638        }
639
640        for my $l (qw( debu inf foo bar )) {
641            ok( !Log::Dispatch->level_is_valid($l), "$l is not valid level" );
642        }
643
644        #   Provide calling line if level missing
645        my $string;
646        my $dispatch = Log::Dispatch->new(
647            outputs => [
648                [
649                    'String',
650                    name      => 'string',
651                    string    => \$string,
652                    min_level => 'debug',
653                ],
654            ],
655        );
656
657        like(
658            exception { $dispatch->log( msg => 'Message' ) },
659            qr/Logging level was not provided at .* line \d+./,
660            'Provide calling line if level not provided'
661        );
662    }
663);
664
665subtest(
666    'Log::Dispatch->would_log method',
667    sub {
668        my $string;
669        my $dispatch = Log::Dispatch->new(
670            outputs => [
671                [
672                    'String',
673                    name      => 'string',
674                    string    => \$string,
675                    min_level => 'debug',
676                ],
677            ],
678        );
679
680        is(
681            $dispatch->would_log('debug'),
682            1,
683            'Would log works with level name'
684        );
685
686        is(
687            $dispatch->would_log(0),
688            1,
689            'Would log works with level number'
690        );
691    }
692);
693
694subtest(
695    'File output mode=write',
696    sub {
697        my $mode_log = File::Spec->catfile( $tempdir, 'mode.log' );
698
699        my $f1 = Log::Dispatch::File->new(
700            name      => 'file',
701            min_level => 1,
702            filename  => $mode_log,
703            mode      => 'write',
704        );
705        $f1->log(
706            level   => 'emerg',
707            message => "test2\n"
708        );
709
710        undef $f1;
711
712        open my $fh, '<', $mode_log
713            or die "Cannot read $mode_log: $!";
714        my $data = do { local $/ = undef; <$fh> };
715        close $fh or die $!;
716
717        like( $data, qr/^test2/, 'test write mode' );
718    }
719);
720
721subtest(
722    'Log::Dispatch->dispatch by name',
723    sub {
724        my $dispatch = Log::Dispatch->new;
725
726        $dispatch->add(
727            Log::Dispatch::Screen->new(
728                name      => 'yomama',
729                min_level => 'alert'
730            )
731        );
732
733        ok(
734            $dispatch->output('yomama'),
735            'yomama output should exist'
736        );
737
738        ok(
739            !$dispatch->output('nomama'),
740            'nomama output should not exist'
741        );
742    }
743);
744
745subtest(
746    'File output close_after_writer & permissions',
747    sub {
748        my $dispatch = Log::Dispatch->new;
749
750        my $close_log = File::Spec->catfile( $tempdir, 'close.log' );
751
752        ## no critic (ValuesAndExpressions::ProhibitLeadingZeros)
753        $dispatch->add(
754            Log::Dispatch::File->new(
755                name              => 'close',
756                min_level         => 'info',
757                filename          => $close_log,
758                permissions       => 0777,
759                close_after_write => 1
760            )
761        );
762
763        $dispatch->log( level => 'info', message => "info\n" );
764
765        open my $fh, '<', $close_log
766            or die "Can't read $close_log: $!";
767        my @log = <$fh>;
768        close $fh or die $!;
769
770        is(
771            $log[0], "info\n",
772            q{First line in log file should be 'info\\n'}
773        );
774
775        my $mode = ( stat $close_log )[2]
776            or die "Cannot stat $close_log: $!";
777
778        my $mode_string = sprintf( '%04o', $mode & 07777 );
779
780        if ( $^O =~ /win32/i ) {
781            ok(
782                $mode_string eq '0777' || $mode_string eq '0666',
783                'Mode should be 0777 or 0666'
784            );
785        }
786        elsif ( $^O =~ /cygwin|msys/i ) {
787            ok(
788                $mode_string eq '0777' || $mode_string eq '0644',
789                'Mode should be 0777 or 0644'
790            );
791        }
792        else {
793            is(
794                $mode_string,
795                '0777',
796                'Mode should be 0777'
797            );
798        }
799    }
800);
801
802subtest(
803    'File output chmod calls',
804    sub {
805        my $dispatch = Log::Dispatch->new;
806
807        my $chmod_log = File::Spec->catfile( $tempdir, 'chmod.log' );
808
809        open my $fh, '>', $chmod_log
810            or die "Cannot write to $chmod_log: $!";
811        close $fh or die $!;
812
813        chmod 0777, $chmod_log
814            or die "Cannot chmod 0777 $chmod_log: $!";
815
816        my @chmod;
817        ## no critic (TestingAndDebugging::ProhibitNoWarnings)
818        no warnings 'once';
819        local *CORE::GLOBAL::chmod = sub { @chmod = @_; warn @chmod };
820
821        ## no critic (ValuesAndExpressions::ProhibitLeadingZeros)
822        $dispatch->add(
823            Log::Dispatch::File->new(
824                name        => 'chmod',
825                min_level   => 'info',
826                filename    => $chmod_log,
827                permissions => 0777,
828            )
829        );
830
831        $dispatch->warning('test');
832
833        ok(
834            !scalar @chmod,
835            'chmod() was not called when permissions already matched what was specified'
836        );
837    }
838);
839
840subtest(
841    'File output binmode',
842    sub {
843    SKIP:
844        {
845            ## no critic (ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions)
846            skip "Cannot test utf8 files with this version of Perl ($])", 1
847                unless $] >= 5.008;
848
849            my $dispatch = Log::Dispatch->new;
850
851            my $utf8_log = File::Spec->catfile( $tempdir, 'utf8.log' );
852
853            $dispatch->add(
854                Log::Dispatch::File->new(
855                    name      => 'utf8',
856                    min_level => 'info',
857                    filename  => $utf8_log,
858                    binmode   => ':encoding(UTF-8)',
859                )
860            );
861
862            my @warnings;
863
864            {
865                local $SIG{__WARN__} = sub { push @warnings, @_ };
866                $dispatch->warning("\x{999A}");
867            }
868
869            ok(
870                !scalar @warnings,
871                'utf8 binmode was applied to file and no warnings were issued'
872            );
873        }
874    }
875);
876
877subtest(
878    'Log::Dispatch->would_log',
879    sub {
880        my $dispatch = Log::Dispatch->new;
881
882        $dispatch->add(
883            Log::Dispatch::Null->new(
884                name      => 'null',
885                min_level => 'warning',
886            )
887        );
888
889        ok(
890            !$dispatch->would_log('foo'),
891            q{will not log 'foo'}
892        );
893
894        ok(
895            !$dispatch->would_log('debug'),
896            q{will not log 'debug'}
897        );
898
899        ok(
900            !$dispatch->is_debug(),
901            'is_debug returns false'
902        );
903
904        ok(
905            $dispatch->is_warning(),
906            'is_warning returns true'
907        );
908
909        ok(
910            $dispatch->would_log('crit'),
911            q{will log 'crit'}
912        );
913
914        ok(
915            $dispatch->is_crit,
916            q{will log 'crit'}
917        );
918    }
919);
920
921subtest(
922    'messages as coderefs are only called as needed',
923    sub {
924        my $dispatch = Log::Dispatch->new;
925
926        $dispatch->add(
927            Log::Dispatch::Null->new(
928                name      => 'null',
929                min_level => 'info',
930                max_level => 'critical',
931            )
932        );
933
934        my $called  = 0;
935        my $message = sub { $called = 1 };
936
937        $dispatch->log( level => 'debug', message => $message );
938        ok(
939            !$called,
940            'subref is not called if the message would not be logged'
941        );
942
943        $called = 0;
944        $dispatch->log( level => 'warning', message => $message );
945        ok( $called, 'subref is called when message is logged' );
946
947        $called = 0;
948        $dispatch->log( level => 'emergency', message => $message );
949        ok(
950            !$called,
951            'subref is not called when message would not be logged'
952        );
953    }
954);
955
956subtest(
957    'passing coderef to ->log',
958    sub {
959        my $string;
960        my $dispatch = Log::Dispatch->new;
961        $dispatch->add(
962            Log::Dispatch::String->new(
963                name      => 'handle',
964                string    => \$string,
965                min_level => 'debug',
966            )
967        );
968
969        $dispatch->log(
970            level   => 'debug',
971            message => sub {'this is my message'},
972        );
973
974        is(
975            $string, 'this is my message',
976            'message returned by subref is logged'
977        );
978    }
979);
980
981subtest(
982    'newline parameter to output',
983    sub {
984        my $string;
985        my $dispatch = Log::Dispatch->new;
986        $dispatch->add(
987            Log::Dispatch::String->new(
988                name      => 'handle',
989                string    => \$string,
990                min_level => 'debug',
991                newline   => 1,
992            )
993        );
994        $dispatch->debug('hello');
995        $dispatch->debug('goodbye');
996
997        is( $string, "hello\ngoodbye\n", 'added newlines' );
998    }
999);
1000
1001subtest(
1002    'log_and_die method',
1003    sub {
1004        my $string;
1005        my $dispatch = Log::Dispatch->new;
1006        $dispatch->add(
1007            Log::Dispatch::String->new(
1008                name      => 'handle',
1009                string    => \$string,
1010                min_level => 'debug',
1011            )
1012        );
1013
1014        my $e = exception {
1015            _log_and_die(
1016                $dispatch,
1017                level   => 'error',
1018                message => 'this is my message',
1019            );
1020        };
1021
1022        ok( $e, 'died when calling log_and_die()' );
1023        like( $e, qr{this is my message}, 'error contains expected message' );
1024        like( $e, qr{basic\.t line 50\d\d}, 'error croaked' );
1025
1026        is( $string, 'this is my message', 'message is logged' );
1027
1028        undef $string;
1029
1030        try {
1031            Croaker::croak($dispatch)
1032        }
1033        catch {
1034            $e = $_;
1035        };
1036
1037        ok( $e, 'died when calling log_and_croak()' );
1038        like( $e, qr{croaking a message}, 'error contains expected message' );
1039        like(
1040            $e, qr{basic\.t line 100\d\d},
1041            'error croaked from perspective of caller'
1042        );
1043
1044        is( $string, 'croaking a message', 'message is logged' );
1045    }
1046);
1047
1048subtest(
1049    'adding and removing callbacks in output',
1050    sub {
1051        my $string;
1052        my $dispatch = Log::Dispatch->new;
1053        $dispatch->add(
1054            Log::Dispatch::String->new(
1055                name      => 'handle',
1056                string    => \$string,
1057                min_level => 'debug',
1058            )
1059        );
1060
1061        $dispatch->log( level => 'debug', message => 'foo' );
1062        is( $string, 'foo', 'first test w/o callback' );
1063
1064        my $cb = sub { return 'bar' };
1065        $string = q{};
1066        $dispatch->add_callback($cb);
1067        $dispatch->log( level => 'debug', message => 'foo' );
1068        is( $string, 'bar', 'second call, callback overrides message' );
1069
1070        $string = q{};
1071        $dispatch->remove_callback($cb);
1072        $dispatch->log( level => 'debug', message => 'foo' );
1073        is( $string, 'foo', 'third call, callback is removed' );
1074    }
1075);
1076
1077subtest(
1078    'adding and removing callbacks in Log::Dispatch',
1079    sub {
1080        my $string;
1081        my $dispatch = Log::Dispatch->new(
1082            callbacks => sub { return 'baz' },
1083        );
1084        $dispatch->add(
1085            Log::Dispatch::String->new(
1086                name      => 'handle',
1087                string    => \$string,
1088                min_level => 'debug',
1089            )
1090        );
1091
1092        $dispatch->log( level => 'debug', message => 'foo' );
1093        is( $string, 'baz', 'first test gets orig callback result' );
1094
1095        my $cb = sub { return 'bar' };
1096        $string = q{};
1097        $dispatch->add_callback($cb);
1098        $dispatch->log( level => 'debug', message => 'foo' );
1099        is( $string, 'bar', 'second call, callback overrides message' );
1100
1101        $string = q{};
1102        $dispatch->remove_callback($cb);
1103        $dispatch->log( level => 'debug', message => 'foo' );
1104        is( $string, 'baz', 'third call, output callback is removed' );
1105    }
1106);
1107
1108subtest(
1109    'callback in output can overwrite message',
1110    sub {
1111        my $string;
1112        my $dispatch = Log::Dispatch->new;
1113        $dispatch->add(
1114            Log::Dispatch::String->new(
1115                name      => 'handle',
1116                string    => \$string,
1117                min_level => 'debug',
1118            )
1119        );
1120
1121        $dispatch->log( level => 'debug', message => 'foo' );
1122        is( $string, 'foo', 'first test w/o callback' );
1123
1124        $string = q{};
1125        $dispatch->add_callback( sub { return 'bar' } );
1126        $dispatch->log( level => 'debug', message => 'foo' );
1127        is( $string, 'bar', 'second call, callback overrides message' );
1128    }
1129);
1130
1131subtest(
1132    'callback in Log::Dispatch can overwrite message',
1133    sub {
1134        my $string;
1135        my $dispatch = Log::Dispatch->new(
1136            callbacks => sub { return 'baz' },
1137        );
1138        $dispatch->add(
1139            Log::Dispatch::String->new(
1140                name      => 'handle',
1141                string    => \$string,
1142                min_level => 'debug',
1143            )
1144        );
1145
1146        $dispatch->log( level => 'debug', message => 'foo' );
1147        is( $string, 'baz', 'first test gets orig callback result' );
1148
1149        $string = q{};
1150        $dispatch->add_callback( sub { return 'bar' } );
1151        $dispatch->log( level => 'debug', message => 'foo' );
1152        is( $string, 'bar', 'second call, callback overrides message' );
1153    }
1154);
1155
1156subtest(
1157    'default output name',
1158    sub {
1159
1160        # Test defaults
1161        my $dispatch = Log::Dispatch::Null->new( min_level => 'debug' );
1162        like( $dispatch->name, qr/anon/, 'generated anon name' );
1163        is( $dispatch->max_level, 'emergency', 'max_level is emergency' );
1164    }
1165);
1166
1167subtest(
1168    'callbacks get correct level',
1169    sub {
1170        my $level;
1171        my $record_level = sub {
1172            my %p = @_;
1173            $level = $p{level};
1174            return %p;
1175        };
1176
1177        my $dispatch = Log::Dispatch->new(
1178            callbacks => $record_level,
1179            outputs   => [
1180                [
1181                    'Null',
1182                    name      => 'null',
1183                    min_level => 'debug',
1184                ],
1185            ],
1186        );
1187
1188        $dispatch->warn('foo');
1189        is(
1190            $level,
1191            'warning',
1192            'level for call to ->warn is warning'
1193        );
1194
1195        $dispatch->err('foo');
1196        is(
1197            $level,
1198            'error',
1199            'level for call to ->err is error'
1200        );
1201
1202        $dispatch->crit('foo');
1203        is(
1204            $level,
1205            'critical',
1206            'level for call to ->crit is critical'
1207        );
1208
1209        $dispatch->emerg('foo');
1210        is(
1211            $level,
1212            'emergency',
1213            'level for call to ->emerg is emergency'
1214        );
1215    }
1216);
1217
1218subtest(
1219    'Code output',
1220    sub {
1221        my @calls;
1222        my $log = Log::Dispatch->new(
1223            outputs => [
1224                [
1225                    'Code',
1226                    min_level => 'error',
1227                    code      => sub { push @calls, {@_} },
1228                ],
1229            ]
1230        );
1231
1232        $log->error('foo');
1233        $log->info('bar');
1234        $log->critical('baz');
1235
1236        is_deeply(
1237            \@calls,
1238            [
1239                {
1240                    level   => 'error',
1241                    message => 'foo',
1242                }, {
1243                    level   => 'critical',
1244                    message => 'baz',
1245                },
1246            ],
1247            'code received the expected messages'
1248        );
1249    }
1250);
1251
1252subtest(
1253    'passing level as name or integer',
1254    sub {
1255        my $dispatch = Log::Dispatch->new;
1256        my $log      = File::Spec->catdir( $tempdir, 'emerg.log' );
1257
1258        $dispatch->add(
1259            Log::Dispatch::File->new(
1260                name      => 'file1',
1261                min_level => 3,
1262                filename  => $log,
1263            )
1264        );
1265
1266        $dispatch->log( level => 'info',  message => "info level 1\n" );
1267        $dispatch->log( level => 'emerg', message => "emerg level 1\n" );
1268        $dispatch->log( level => 'warn',  message => "warn level 1\n" );
1269        $dispatch->log( level => 3,       message => "bug 106495 1\n" );
1270        $dispatch->log( level => 4,       message => "bug 106495 2\n" );
1271        $dispatch->log( level => 1,       message => "bug 106495 3\n" );
1272
1273        open my $fh, '<', $log or die $!;
1274        my @log = <$fh>;
1275        close $fh or die $!;
1276
1277        is( $log[0], "emerg level 1\n", 'at level 3, emerg works' );
1278        is( $log[1], "warn level 1\n",  'at level 3, warn works' );
1279        is(
1280            $log[2], "bug 106495 1\n",
1281            'level as integer works with min_level 3 and level 3'
1282        );
1283        is(
1284            $log[3], "bug 106495 2\n",
1285            'level as integer works with min_level 3 and level 4'
1286        );
1287        is(
1288            $log[4], undef,
1289            'using integer level works with min_level 3 and level 1'
1290        );
1291    }
1292);
1293
1294subtest(
1295    'more levels as integers',
1296    sub {
1297        my $dispatch = Log::Dispatch->new;
1298        my $log      = File::Spec->catdir( $tempdir, 'emerg.log' );
1299
1300        $dispatch->add(
1301            Log::Dispatch::File->new(
1302                name      => 'file1',
1303                min_level => 0,
1304                filename  => $log,
1305            )
1306        );
1307
1308        $dispatch->log( level => 0, message => "bug 106495 0\n" );
1309        $dispatch->log( level => 1, message => "bug 106495 1\n" );
1310        $dispatch->log( level => 2, message => "bug 106495 2\n" );
1311        $dispatch->log( level => 3, message => "bug 106495 3\n" );
1312        $dispatch->log( level => 4, message => "bug 106495 4\n" );
1313        $dispatch->log( level => 5, message => "bug 106495 5\n" );
1314        $dispatch->log( level => 6, message => "bug 106495 6\n" );
1315        $dispatch->log( level => 7, message => "bug 106495 7\n" );
1316
1317        open my $fh, '<', $log or die $!;
1318        my @log = <$fh>;
1319        close $fh or die $!;
1320
1321        is( $log[0], "bug 106495 0\n", 'at level 0, int works' );
1322        is( $log[1], "bug 106495 1\n", 'at level 1, int works' );
1323        is( $log[2], "bug 106495 2\n", 'at level 2, int works' );
1324        is( $log[3], "bug 106495 3\n", 'at level 3, int works' );
1325        is( $log[4], "bug 106495 4\n", 'at level 4, int works' );
1326        is( $log[5], "bug 106495 5\n", 'at level 5, int works' );
1327        is( $log[6], "bug 106495 6\n", 'at level 6, int works' );
1328        is( $log[7], "bug 106495 7\n", 'at level 7, int works' );
1329    }
1330);
1331
1332done_testing();
1333
1334## no critic (Modules::ProhibitMultiplePackages)
1335{
1336    package Log::Dispatch::String;
1337
1338    use strict;
1339
1340    use Log::Dispatch::Output;
1341
1342    use base qw( Log::Dispatch::Output );
1343
1344    sub new {
1345        my $proto = shift;
1346        my $class = ref $proto || $proto;
1347        my %p     = @_;
1348
1349        my $self = bless { string => $p{string} }, $class;
1350
1351        $self->_basic_init(%p);
1352
1353        return $self;
1354    }
1355
1356    sub log_message {
1357        my $self = shift;
1358        my %p    = @_;
1359
1360        ${ $self->{string} } .= $p{message};
1361    }
1362}
1363
1364#line 5000
1365sub _log_and_die {
1366    shift->log_and_die(@_);
1367}
1368
1369{
1370#line 10000
1371    package Croaker;
1372
1373    sub croak {
1374        shift->log_and_croak(
1375            level   => 'error',
1376            message => 'croaking a message'
1377        );
1378    }
1379}
1380