use strict;
use warnings;
use Test::More tests => 144;
use File::Spec;
use File::Temp qw( tempdir );
use Log::Dispatch;
my %tests;
BEGIN
{
foreach ( qw( MailSend MIMELite MailSendmail MailSender ) )
{
eval "use Log::Dispatch::Email::$_";
$tests{$_} = ! $@;
}
eval "use Log::Dispatch::Syslog";
$tests{Syslog} = ! $@;
}
my %TestConfig;
if ( -d '.svn' )
{
%TestConfig = ( email_address => 'autarch@urth.org',
syslog_file => '/var/log/messages',
);
}
use Log::Dispatch::File;
use Log::Dispatch::Handle;
use Log::Dispatch::Null;
use Log::Dispatch::Screen;
use IO::File;
my $tempdir = tempdir( CLEANUP => 1 );
my $dispatch = Log::Dispatch->new;
ok( $dispatch, "created Log::Dispatch object" );
# Test Log::Dispatch::File
{
my $emerg_log = File::Spec->catdir( $tempdir, 'emerg.log' );
$dispatch->add( Log::Dispatch::File->new( name => 'file1',
min_level => 'emerg',
filename => $emerg_log ) );
$dispatch->log( level => 'info', message => "info level 1\n" );
$dispatch->log( level => 'emerg', message => "emerg level 1\n" );
my $debug_log = File::Spec->catdir( $tempdir, 'debug.log' );
$dispatch->add( Log::Dispatch::File->new( name => 'file2',
min_level => 'debug',
filename => $debug_log ) );
$dispatch->log( level => 'info', message => "info level 2\n" );
$dispatch->log( level => 'emerg', message => "emerg level 2\n" );
# This'll close them filehandles!
undef $dispatch;
open my $emerg_fh, '<', $emerg_log
or die "Can't read $emerg_log: $!";
open my $debug_fh, '<', $debug_log
or die "Can't read $debug_log: $!";
my @log = <$emerg_fh>;
is( $log[0], "emerg level 1\n",
"First line in log file set to level 'emerg' is 'emerg level 1'" );
is( $log[1], "emerg level 2\n",
"Second line in log file set to level 'emerg' is 'emerg level 2'" );
@log = <$debug_fh>;
is( $log[0], "info level 2\n",
"First line in log file set to level 'debug' is 'info level 2'" );
is( $log[1], "emerg level 2\n",
"Second line in log file set to level 'debug' is 'emerg level 2'" );
}
# max_level test
{
my $max_log = File::Spec->catfile( $tempdir, 'max.log' );
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::File->new( name => 'file1',
min_level => 'debug',
max_level => 'crit',
filename => $max_log ) );
$dispatch->log( level => 'emerg', message => "emergency\n" );
$dispatch->log( level => 'crit', message => "critical\n" );
undef $dispatch; # close file handles
open my $fh, '<', $max_log
or die "Can't read $max_log: $!";
my @log = <$fh>;
is( $log[0], "critical\n",
"First line in log file with a max level of 'crit' is 'critical'" );
}
# Log::Dispatch::Handle test
{
my $handle_log = File::Spec->catfile( $tempdir, 'handle.log' );
my $fh = IO::File->new( $handle_log, 'w' )
or die "Can't write to $handle_log: $!";
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Handle->new( name => 'handle',
min_level => 'debug',
handle => $fh ) );
$dispatch->log( level => 'notice', message => "handle test\n" );
# close file handles
undef $dispatch;
undef $fh;
open $fh, '<', $handle_log
or die "Can't open $handle_log: $!";
my @log = <$fh>;
close $fh;
is( $log[0], "handle test\n",
"Log::Dispatch::Handle created log file should contain 'handle test\\n'" );
}
# Log::Dispatch::Email::MailSend
SKIP:
{
skip "Cannot do MailSend tests", 1
unless $tests{MailSend} && $TestConfig{email_address};
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Email::MailSend->new( name => 'Mail::Send',
min_level => 'debug',
to => $TestConfig{email_address},
subject => 'Log::Dispatch test suite' ) );
$dispatch->log( level => 'emerg', message => "Mail::Send test - If you can read this then the test succeeded (PID $$)" );
diag( "Sending email with Mail::Send to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n" );
undef $dispatch;
ok( 1, 'sent email via MailSend' );
}
# Log::Dispatch::Email::MailSendmail
SKIP:
{
skip "Cannot do MailSendmail tests", 1
unless $tests{MailSendmail} && $TestConfig{email_address};
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Email::MailSendmail->new( name => 'Mail::Sendmail',
min_level => 'debug',
to => $TestConfig{email_address},
subject => 'Log::Dispatch test suite' ) );
$dispatch->log( level => 'emerg', message => "Mail::Sendmail test - If you can read this then the test succeeded (PID $$)" );
diag( "Sending email with Mail::Sendmail to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n" );
undef $dispatch;
ok( 1, 'sent email via MailSendmail' );
}
# Log::Dispatch::Email::MIMELite
SKIP:
{
skip "Cannot do MIMELite tests", 1
unless $tests{MIMELite} && $TestConfig{email_address};
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Email::MIMELite->new( name => 'Mime::Lite',
min_level => 'debug',
to => $TestConfig{email_address},
subject => 'Log::Dispatch test suite' ) );
$dispatch->log( level => 'emerg', message => "MIME::Lite - If you can read this then the test succeeded (PID $$)" );
diag( "Sending email with MIME::Lite to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n" );
undef $dispatch;
ok( 1, 'sent mail via MIMELite' );
}
# Log::Dispatch::Screen
{
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Screen->new( name => 'screen',
min_level => 'debug',
stderr => 0 ) );
my $text;
tie *STDOUT, 'Test::Tie::STDOUT', \$text;
$dispatch->log( level => 'crit', message => 'testing screen' );
untie *STDOUT;
is( $text, 'testing screen',
"Log::Dispatch::Screen outputs to STDOUT" );
}
# Log::Dispatch::Output->accepted_levels
{
my $l = Log::Dispatch::Screen->new( name => 'foo',
min_level => 'warning',
max_level => 'alert',
stderr => 0 );
my @expected = qw(warning error critical alert);
my @levels = $l->accepted_levels;
my $pass = 1;
for (my $x = 0; $x < scalar @expected; $x++)
{
$pass = 0 unless $expected[$x] eq $levels[$x];
}
is( scalar @expected, scalar @levels,
"number of levels matched" );
ok( $pass, "levels matched" );
}
# Log::Dispatch single callback
{
my $reverse = sub { my %p = @_; return reverse $p{message}; };
my $dispatch = Log::Dispatch->new( callbacks => $reverse );
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => 'warning',
max_level => 'alert',
) );
$dispatch->log( level => 'warning', message => 'esrever' );
is( $string, 'reverse',
"callback to reverse text" );
}
# Log::Dispatch multiple callbacks
{
my $reverse = sub { my %p = @_; return reverse $p{message}; };
my $uc = sub { my %p = @_; return uc $p{message}; };
my $dispatch = Log::Dispatch->new( callbacks => [ $reverse, $uc ] );
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => 'warning',
max_level => 'alert',
) );
$dispatch->log( level => 'warning', message => 'esrever' );
is( $string, 'REVERSE',
"callback to reverse and uppercase text" );
}
# Log::Dispatch::Output single callback
{
my $reverse = sub { my %p = @_; return reverse $p{message}; };
my $dispatch = Log::Dispatch->new;
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => 'warning',
max_level => 'alert',
callbacks => $reverse ) );
$dispatch->log( level => 'warning', message => 'esrever' );
is( $string, 'reverse',
"Log::Dispatch::Output callback to reverse text" );
}
# Log::Dispatch::Output multiple callbacks
{
my $reverse = sub { my %p = @_; return reverse $p{message}; };
my $uc = sub { my %p = @_; return uc $p{message}; };
my $dispatch = Log::Dispatch->new;
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => 'warning',
max_level => 'alert',
callbacks => [ $reverse, $uc ] ) );
$dispatch->log( level => 'warning', message => 'esrever' );
is( $string, 'REVERSE',
"Log::Dispatch::Output callbacks to reverse and uppercase text" );
}
# test level paramter to callbacks
{
my $level = sub { my %p = @_; return uc $p{level}; };
my $dispatch = Log::Dispatch->new( callbacks => $level );
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => 'warning',
max_level => 'alert',
stderr => 0 ) );
$dispatch->log( level => 'warning', message => 'esrever' );
is( $string, 'WARNING',
"Log::Dispatch callback to uppercase the level parameter" );
}
# Comprehensive test of new methods that match level names
{
my %levels = map { $_ => $_ } ( qw( debug info notice warning error critical alert emergency ) );
@levels{ qw( err crit emerg ) } = ( qw( error critical emergency ) );
foreach my $allowed_level ( qw( debug info notice warning error critical alert emergency ) )
{
my $dispatch = Log::Dispatch->new;
my $string;
$dispatch->add( Log::Dispatch::String->new( name => 'foo',
string => \$string,
min_level => $allowed_level,
max_level => $allowed_level,
) );
foreach my $test_level ( qw( debug info notice warning err
error crit critical alert emerg emergency ) )
{
$string = '';
$dispatch->$test_level( $test_level, 'test' );
if ( $levels{$test_level} eq $allowed_level )
{
my $expect = join $", $test_level, 'test';
is( $string, $expect,
"Calling $test_level method should send message '$expect'" );
}
else
{
ok( ! length $string,
"Calling $test_level method should not log anything" );
}
}
}
}
# Log::Dispatch->level_is_valid method
{
foreach my $l ( qw( debug info notice warning err error
crit critical alert emerg emergency ) )
{
ok( Log::Dispatch->level_is_valid($l), "$l is valid level" );
}
foreach my $l ( qw( debu inf foo bar ) )
{
ok( ! Log::Dispatch->level_is_valid($l), "$l is not valid level" );
}
}
# make sure passing mode as write works
{
my $mode_log = File::Spec->catfile( $tempdir, 'mode.log' );
my $f1 = Log::Dispatch::File->new( name => 'file',
min_level => 1,
filename => $mode_log,
mode => 'write',
);
$f1->log( level => 'emerg',
message => "test2\n" );
undef $f1;
open my $fh, '<', $mode_log
or die "Cannot read $mode_log: $!";
my $data = join '', <$fh>;
close $fh;
like( $data, qr/^test2/, "test write mode" );
}
# Log::Dispatch::Email::MailSender
SKIP:
{
skip "Cannot do MailSender tests", 1
unless $tests{MailSender} && $TestConfig{email_address};
my $dispatch = Log::Dispatch->new;
$dispatch->add
( Log::Dispatch::Email::MailSender->new
( name => 'Mail::Sender',
min_level => 'debug',
smtp => 'localhost',
to => $TestConfig{email_address},
subject => 'Log::Dispatch test suite' ) );
$dispatch->log( level => 'emerg', message => "Mail::Sender - If you can read this then the test succeeded (PID $$)" );
diag( "Sending email with Mail::Sender to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n" );
undef $dispatch;
ok( 1, 'sent email via MailSender' );
}
# dispatcher exists
{
my $dispatch = Log::Dispatch->new;
$dispatch->add
( Log::Dispatch::Screen->new( name => 'yomama',
min_level => 'alert' ) );
ok( $dispatch->output('yomama'),
"yomama output should exist" );
ok( ! $dispatch->output('nomama'),
"nomama output should not exist" );
}
# Test Log::Dispatch::File - close_after_write & permissions
{
my $dispatch = Log::Dispatch->new;
my $close_log = File::Spec->catfile( $tempdir, 'close.log' );
$dispatch->add( Log::Dispatch::File->new( name => 'close',
min_level => 'info',
filename => $close_log,
permissions => 0777,
close_after_write => 1 ) );
$dispatch->log( level => 'info', message => "info\n" );
open my $fh, '<', $close_log
or die "Can't read $close_log: $!";
my @log = <$fh>;
close $fh;
is( $log[0], "info\n",
"First line in log file should be 'info\\n'" );
my $mode = ( stat $close_log )[2]
or die "Cannot stat $close_log: $!";
my $mode_string = sprintf( '%04o', $mode & 07777 );
if( $^O =~ /win32/i )
{
ok( $mode_string == '0777' || $mode_string == '0666',
"Mode should be 0777 or 0666");
}
else
{
is( $mode_string, '0777',
"Mode should be 0777" );
}
}
{
my $dispatch = Log::Dispatch->new;
my $chmod_log = File::Spec->catfile( $tempdir, 'chmod.log' );
open my $fh, '>', $chmod_log
or die "Cannot write to $chmod_log: $!";
close $fh;
chmod 0777, $chmod_log
or die "Cannot chmod 0777 $chmod_log: $!";
my @chmod;
no warnings 'once';
local *CORE::chmod = sub { @chmod = @_; warn @chmod };
$dispatch->add( Log::Dispatch::File->new( name => 'chmod',
min_level => 'info',
filename => $chmod_log,
permissions => 0777,
) );
$dispatch->warning('test');
ok( ! scalar @chmod,
'chmod() was not called when permissions already matched what was specified' );
}
SKIP:
{
skip "Cannot test utf8 files with this version of Perl ($])", 1
unless $] >= 5.008;
my $dispatch = Log::Dispatch->new;
my $utf8_log = File::Spec->catfile( $tempdir, 'utf8.log' );
$dispatch->add( Log::Dispatch::File->new( name => 'utf8',
min_level => 'info',
filename => $utf8_log,
binmode => ':utf8',
) );
my @warnings;
{
local $SIG{__WARN__} = sub { push @warnings, @_ };
$dispatch->warning("\x{999A}");
}
ok( ! scalar @warnings,
'utf8 binmode was applied to file and no warnings were issued' );
}
# would_log
{
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Null->new( name => 'null',
min_level => 'warning',
) );
ok( ! $dispatch->would_log('foo'),
"will not log 'foo'" );
ok( ! $dispatch->would_log('debug'),
"will not log 'debug'" );
ok( $dispatch->would_log('crit'),
"will log 'crit'" );
}
{
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::Null->new( name => 'null',
min_level => 'info',
max_level => 'critical',
) );
my $called = 0;
my $message = sub { $called = 1 };
$dispatch->log( level => 'debug', message => $message );
ok( ! $called, 'subref is not called if the message would not be logged' );
$called = 0;
$dispatch->log( level => 'warning', message => $message );
ok( $called, 'subref is called when message is logged' );
$called = 0;
$dispatch->log( level => 'emergency', message => $message );
ok( ! $called, 'subref is not called when message would not be logged' );
}
{
my $string;
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::String->new( name => 'handle',
string => \$string,
min_level => 'debug',
) );
$dispatch->log( level => 'debug',
message => sub { 'this is my message' },
);
is( $string, 'this is my message', 'message returned by subref is logged' );
}
{
my $string;
my $dispatch = Log::Dispatch->new;
$dispatch->add( Log::Dispatch::String->new( name => 'handle',
string => \$string,
min_level => 'debug',
) );
eval
{
$dispatch->log_and_die( level => 'error',
message => 'this is my message',
);
};
my $e = $@;
ok( $e, 'died when calling log_and_die()' );
like( $e, qr{this is my message}, 'error contains expected message' );
like( $e, qr{01-basic\.t line 614}, 'error croaked' );
is( $string, 'this is my message', 'message is logged' );
undef $string;
eval
{
Croaker::croak($dispatch);
};
$e = $@;
ok( $e, 'died when calling log_and_croak()' );
like( $e, qr{croak}, 'error contains expected message' );
like( $e, qr{01-basic\.t line 680}, 'error croaked from perspective of caller' );
is( $string, 'croak', 'message is logged' );
}
package Log::Dispatch::String;
use strict;
use Log::Dispatch::Output;
use base qw( Log::Dispatch::Output );
sub new
{
my $proto = shift;
my $class = ref $proto || $proto;
my %p = @_;
my $self = bless { string => $p{string} }, $class;
$self->_basic_init(%p);
return $self;
}
sub log_message
{
my $self = shift;
my %p = @_;
${ $self->{string} } .= $p{message};
}
package Croaker;
sub croak
{
my $log = shift;
$log->log_and_croak( level => 'error', message => 'croak' );
}
# Used for testing Log::Dispatch::Screen
package Test::Tie::STDOUT;
sub TIEHANDLE
{
my $class = shift;
my $self = {};
$self->{string} = shift;
${ $self->{string} } ||= '';
return bless $self, $class;
}
sub PRINT
{
my $self = shift;
${ $self->{string} } .= join '', @_;
}
sub PRINTF
{
my $self = shift;
my $format = shift;
${ $self->{string} } .= sprintf($format, @_);
}