summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-06-14 16:34:55 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-06-14 16:34:55 +0000
commit2e0d2bec52bd345ef05ea12ea9052643ef135029 (patch)
treed128cd9759bc672fa01bac1561911aa8a895981b /t
downloadLog-Dispatch-tarball-2e0d2bec52bd345ef05ea12ea9052643ef135029.tar.gz
Diffstat (limited to 't')
-rw-r--r--t/00-compile.t38
-rw-r--r--t/00-report-prereqs.dd60
-rw-r--r--t/00-report-prereqs.t183
-rw-r--r--t/01-basic.t1195
-rw-r--r--t/02-email-exit.t15
-rw-r--r--t/03-short-syntax.t77
-rw-r--r--t/04-binmode.t54
-rw-r--r--t/05-close-after-write.t97
-rw-r--r--t/06-syslog.t66
-rw-r--r--t/author-eol.t60
-rw-r--r--t/author-no-tabs.t60
-rw-r--r--t/author-pod-spell.t97
-rwxr-xr-xt/email-exit-helper.pl20
-rw-r--r--t/lib/Log/Dispatch/TestUtil.pm44
-rw-r--r--t/release-cpan-changes.t19
-rw-r--r--t/release-pod-coverage.t63
-rw-r--r--t/release-pod-no404s.t29
-rw-r--r--t/release-pod-syntax.t14
-rw-r--r--t/release-portability.t20
-rw-r--r--t/release-test-version.t46
-rw-r--r--t/release-tidyall.t17
-rwxr-xr-xt/sendmail3
22 files changed, 2277 insertions, 0 deletions
diff --git a/t/00-compile.t b/t/00-compile.t
new file mode 100644
index 0000000..9e006b4
--- /dev/null
+++ b/t/00-compile.t
@@ -0,0 +1,38 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+my %deps = (
+ ApacheLog => 'Apache::Log',
+ Code => q{},
+ File => q{},
+ 'File::Locked' => q{},
+ Handle => q{},
+ Null => q{},
+ Screen => q{},
+ Syslog => 'Sys::Syslog 0.28',
+ 'Email::MailSend' => 'Mail::Send',
+ 'Email::MIMELite' => 'MIME::Lite',
+ 'Email::MailSendmail' => 'Mail::Sendmail',
+ 'Email::MailSender' => 'Mail::Sender',
+);
+
+use_ok('Log::Dispatch');
+
+for my $subclass ( sort keys %deps ) {
+ my $module = "Log::Dispatch::$subclass";
+
+ if ( !$deps{$subclass}
+ || ( eval "use $deps{$subclass}; 1" && !$@ ) ) {
+ use_ok($module);
+ }
+ else {
+ SKIP:
+ {
+ skip "Cannot load $module without $deps{$subclass}", 1;
+ }
+ }
+}
+
+done_testing();
diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd
new file mode 100644
index 0000000..0f214b2
--- /dev/null
+++ b/t/00-report-prereqs.dd
@@ -0,0 +1,60 @@
+do { my $x = {
+ 'configure' => {
+ 'requires' => {
+ 'Dist::CheckConflicts' => '0.02',
+ 'ExtUtils::MakeMaker' => '0'
+ }
+ },
+ 'develop' => {
+ 'requires' => {
+ 'Code::TidyAll' => '0.24',
+ 'Perl::Critic' => '1.123',
+ 'Perl::Tidy' => '20140711',
+ 'Pod::Coverage::TrustPod' => '0',
+ 'Test::CPAN::Changes' => '0.19',
+ 'Test::Code::TidyAll' => '0.24',
+ 'Test::EOL' => '0',
+ 'Test::More' => '0.88',
+ 'Test::NoTabs' => '0',
+ 'Test::Pod' => '1.41',
+ 'Test::Pod::Coverage' => '1.08',
+ 'Test::Spelling' => '0.12',
+ 'Test::Version' => '1'
+ }
+ },
+ 'runtime' => {
+ 'requires' => {
+ 'Carp' => '0',
+ 'Devel::GlobalDestruction' => '0',
+ 'Dist::CheckConflicts' => '0.02',
+ 'Fcntl' => '0',
+ 'Module::Runtime' => '0',
+ 'Params::Validate' => '0.15',
+ 'Scalar::Util' => '0',
+ 'Sys::Syslog' => '0.28',
+ 'base' => '0',
+ 'perl' => '5.006',
+ 'strict' => '0',
+ 'warnings' => '0'
+ }
+ },
+ 'test' => {
+ 'recommends' => {
+ 'CPAN::Meta' => '2.120900'
+ },
+ 'requires' => {
+ 'Data::Dumper' => '0',
+ 'Exporter' => '0',
+ 'ExtUtils::MakeMaker' => '0',
+ 'File::Spec' => '0',
+ 'File::Temp' => '0',
+ 'IO::File' => '0',
+ 'Test::Fatal' => '0',
+ 'Test::More' => '0.96',
+ 'Test::Requires' => '0',
+ 'lib' => '0'
+ }
+ }
+ };
+ $x;
+ } \ No newline at end of file
diff --git a/t/00-report-prereqs.t b/t/00-report-prereqs.t
new file mode 100644
index 0000000..d8d15ba
--- /dev/null
+++ b/t/00-report-prereqs.t
@@ -0,0 +1,183 @@
+#!perl
+
+use strict;
+use warnings;
+
+# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.021
+
+use Test::More tests => 1;
+
+use ExtUtils::MakeMaker;
+use File::Spec;
+
+# from $version::LAX
+my $lax_version_re =
+ qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
+ |
+ (?:\.[0-9]+) (?:_[0-9]+)?
+ ) | (?:
+ v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
+ |
+ (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
+ )
+ )/x;
+
+# hide optional CPAN::Meta modules from prereq scanner
+# and check if they are available
+my $cpan_meta = "CPAN::Meta";
+my $cpan_meta_pre = "CPAN::Meta::Prereqs";
+my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic
+
+# Verify requirements?
+my $DO_VERIFY_PREREQS = 1;
+
+sub _max {
+ my $max = shift;
+ $max = ( $_ > $max ) ? $_ : $max for @_;
+ return $max;
+}
+
+sub _merge_prereqs {
+ my ($collector, $prereqs) = @_;
+
+ # CPAN::Meta::Prereqs object
+ if (ref $collector eq $cpan_meta_pre) {
+ return $collector->with_merged_prereqs(
+ CPAN::Meta::Prereqs->new( $prereqs )
+ );
+ }
+
+ # Raw hashrefs
+ for my $phase ( keys %$prereqs ) {
+ for my $type ( keys %{ $prereqs->{$phase} } ) {
+ for my $module ( keys %{ $prereqs->{$phase}{$type} } ) {
+ $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module};
+ }
+ }
+ }
+
+ return $collector;
+}
+
+my @include = qw(
+
+);
+
+my @exclude = qw(
+
+);
+
+# Add static prereqs to the included modules list
+my $static_prereqs = do 't/00-report-prereqs.dd';
+
+# Merge all prereqs (either with ::Prereqs or a hashref)
+my $full_prereqs = _merge_prereqs(
+ ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ),
+ $static_prereqs
+);
+
+# Add dynamic prereqs to the included modules list (if we can)
+my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
+if ( $source && $HAS_CPAN_META ) {
+ if ( my $meta = eval { CPAN::Meta->load_file($source) } ) {
+ $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs);
+ }
+}
+else {
+ $source = 'static metadata';
+}
+
+my @full_reports;
+my @dep_errors;
+my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs;
+
+# Add static includes into a fake section
+for my $mod (@include) {
+ $req_hash->{other}{modules}{$mod} = 0;
+}
+
+for my $phase ( qw(configure build test runtime develop other) ) {
+ next unless $req_hash->{$phase};
+ next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING});
+
+ for my $type ( qw(requires recommends suggests conflicts modules) ) {
+ next unless $req_hash->{$phase}{$type};
+
+ my $title = ucfirst($phase).' '.ucfirst($type);
+ my @reports = [qw/Module Want Have/];
+
+ for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) {
+ next if $mod eq 'perl';
+ next if grep { $_ eq $mod } @exclude;
+
+ my $file = $mod;
+ $file =~ s{::}{/}g;
+ $file .= ".pm";
+ my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC;
+
+ my $want = $req_hash->{$phase}{$type}{$mod};
+ $want = "undef" unless defined $want;
+ $want = "any" if !$want && $want == 0;
+
+ my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
+
+ if ($prefix) {
+ my $have = MM->parse_version( File::Spec->catfile($prefix, $file) );
+ $have = "undef" unless defined $have;
+ push @reports, [$mod, $want, $have];
+
+ if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) {
+ if ( $have !~ /\A$lax_version_re\z/ ) {
+ push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)";
+ }
+ elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
+ push @dep_errors, "$mod version '$have' is not in required range '$want'";
+ }
+ }
+ }
+ else {
+ push @reports, [$mod, $want, "missing"];
+
+ if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) {
+ push @dep_errors, "$mod is not installed ($req_string)";
+ }
+ }
+ }
+
+ if ( @reports ) {
+ push @full_reports, "=== $title ===\n\n";
+
+ my $ml = _max( map { length $_->[0] } @reports );
+ my $wl = _max( map { length $_->[1] } @reports );
+ my $hl = _max( map { length $_->[2] } @reports );
+
+ if ($type eq 'modules') {
+ splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl];
+ push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports;
+ }
+ else {
+ splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl];
+ push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports;
+ }
+
+ push @full_reports, "\n";
+ }
+ }
+}
+
+if ( @full_reports ) {
+ diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports;
+}
+
+if ( @dep_errors ) {
+ diag join("\n",
+ "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n",
+ "The following REQUIRED prerequisites were not satisfied:\n",
+ @dep_errors,
+ "\n"
+ );
+}
+
+pass;
+
+# vim: ts=4 sts=4 sw=4 et:
diff --git a/t/01-basic.t b/t/01-basic.t
new file mode 100644
index 0000000..293b098
--- /dev/null
+++ b/t/01-basic.t
@@ -0,0 +1,1195 @@
+use strict;
+use warnings;
+
+use Test::More 0.88;
+use Test::Fatal;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Module::Runtime qw( use_module );
+
+use Log::Dispatch;
+
+my %tests;
+
+BEGIN {
+ local $@;
+ foreach (qw( MailSend MIMELite MailSendmail MailSender )) {
+ eval "use Log::Dispatch::Email::$_";
+ $tests{$_} = !$@;
+ $tests{$_} = 0 if $ENV{LD_NO_MAIL};
+ }
+}
+
+my %TestConfig;
+if ( my $email_address = $ENV{LOG_DISPATCH_TEST_EMAIL} ) {
+ %TestConfig = ( email_address => $email_address );
+}
+
+my @syswrite_strs;
+
+BEGIN {
+ if ( $] >= 5.016 ) {
+ my $syswrite = \&CORE::syswrite;
+ *CORE::GLOBAL::syswrite = sub {
+ my ( $fh, $str, @other ) = @_;
+ push @syswrite_strs, $_[1];
+
+ return $syswrite->( $fh, $str, @other );
+ };
+ }
+}
+
+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',
+ syswrite => 1,
+ filename => $debug_log
+ )
+ );
+
+ my %outputs = map { $_->name() => ref $_ } $dispatch->outputs();
+ is_deeply(
+ \%outputs, {
+ file1 => 'Log::Dispatch::File',
+ file2 => 'Log::Dispatch::File',
+ },
+ '->outputs() method returns all output objects'
+ );
+
+ $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'"
+ );
+
+SKIP:
+ {
+ skip 'This test requires Perl 5.16+', 1
+ unless $] >= 5.016;
+ is_deeply(
+ \@syswrite_strs,
+ [
+ "info level 2\n",
+ "emerg level 2\n",
+ ],
+ 'second LD object used syswrite',
+ );
+ }
+}
+
+# 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"
+ );
+
+ is_deeply(
+ [ $dispatch->callbacks() ],
+ [ $reverse, $uc ],
+ '->callbacks() method returns all of the callback subs'
+ );
+
+ my $clone = $dispatch->clone();
+ is_deeply(
+ $clone,
+ $dispatch,
+ 'clone is a shallow clone of the original object'
+ );
+
+ $clone->add(
+ Log::Dispatch::Screen->new(
+ name => 'screen',
+ min_level => 'debug',
+ )
+ );
+ my @orig_outputs = map { $_->name() } $dispatch->outputs();
+ my @clone_outputs = map { $_->name() } $clone->outputs();
+ isnt(
+ scalar(@orig_outputs),
+ scalar(@clone_outputs),
+ 'clone is not the same as original after adding an output'
+ );
+
+ $clone->add_callback( sub { return 'foo' } );
+ my @orig_cb = $dispatch->callbacks();
+ my @clone_cb = $clone->callbacks();
+ isnt(
+ scalar(@orig_cb),
+ scalar(@clone_cb),
+ 'clone is not the same as original after adding a callback'
+ );
+}
+
+# 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 parameter 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( warn err crit emerg )}
+ = (qw( warning 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 warn 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"
+ );
+ }
+ }
+ }
+}
+
+{
+ my $string;
+ my $dispatch = Log::Dispatch->new(
+ outputs => [
+ [
+ 'String',
+ name => 'string',
+ string => \$string,
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ $dispatch->debug( 'foo', 'bar' );
+ is(
+ $string,
+ 'foo bar',
+ 'passing multiple elements to ->debug stringifies them like an array'
+ );
+
+ $string = q{};
+ $dispatch->debug( sub {'foo'} );
+ is(
+ $string,
+ 'foo',
+ 'passing single sub ref to ->debug calls the sub ref'
+ );
+
+}
+
+# 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" );
+ }
+
+ # Provide calling line if level missing
+ my $string;
+ my $dispatch = Log::Dispatch->new(
+ outputs => [
+ [
+ 'String',
+ name => 'string',
+ string => \$string,
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ like(
+ exception { $dispatch->log( msg => "Message" ) },
+ qr/Logging level was not provided at .* line \d+./,
+ "Provide calling line if level not provided"
+ );
+}
+
+# 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"
+ );
+ }
+ elsif ( $^O =~ /cygwin/i ) {
+ ok(
+ $mode_string == '0777' || $mode_string == '0644',
+ "Mode should be 0777 or 0644"
+ );
+ }
+ 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::GLOBAL::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 => ':encoding(UTF-8)',
+ )
+ );
+
+ 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->is_debug(),
+ 'is_debug returns false'
+ );
+
+ ok(
+ $dispatch->is_warning(),
+ 'is_warning returns true'
+ );
+
+ ok(
+ $dispatch->would_log('crit'),
+ "will log 'crit'"
+ );
+
+ ok(
+ $dispatch->is_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',
+ newline => 1,
+ )
+ );
+ $dispatch->debug('hello');
+ $dispatch->debug('goodbye');
+
+ is( $string, "hello\ngoodbye\n", 'added newlines' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ my $e = exception {
+ $dispatch->log_and_die(
+ level => 'error',
+ message => 'this is my message',
+ );
+ };
+
+ 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 9\d\d}, 'error croaked' );
+
+ is( $string, 'this is my message', 'message is logged' );
+
+ undef $string;
+
+ $e = do {
+ local $@;
+ eval { Croaker::croak($dispatch) };
+ $@;
+ };
+
+ ok( $e, 'died when calling log_and_croak()' );
+ like( $e, qr{croak}, 'error contains expected message' );
+ like(
+ $e, qr{01-basic\.t line 10005},
+ 'error croaked from perspective of caller'
+ );
+
+ is( $string, 'croak', 'message is 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 => 'foo' );
+ is( $string, 'foo', 'first test w/o callback' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => sub { return 'baz' },
+ );
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'baz', 'first test gets orig callback result' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ 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 => 'foo' );
+ is( $string, 'foo', 'first test w/o callback' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => sub { return 'baz' },
+ );
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'baz', 'first test gets orig callback result' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+
+ # Test defaults
+ my $dispatch = Log::Dispatch::Null->new( min_level => 'debug' );
+ like( $dispatch->name, qr/anon/, 'generated anon name' );
+ is( $dispatch->max_level, 'emergency', 'max_level is emergency' );
+}
+
+{
+ my $level;
+ my $record_level = sub {
+ my %p = @_;
+ $level = $p{level};
+ return %p;
+ };
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => $record_level,
+ outputs => [
+ [
+ 'Null',
+ name => 'null',
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ $dispatch->warn('foo');
+ is(
+ $level,
+ 'warning',
+ 'level for call to ->warn is warning'
+ );
+
+ $dispatch->err('foo');
+ is(
+ $level,
+ 'error',
+ 'level for call to ->err is error'
+ );
+
+ $dispatch->crit('foo');
+ is(
+ $level,
+ 'critical',
+ 'level for call to ->crit is critical'
+ );
+
+ $dispatch->emerg('foo');
+ is(
+ $level,
+ 'emergency',
+ 'level for call to ->emerg is emergency'
+ );
+}
+
+{
+ my @calls;
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Code',
+ min_level => 'error',
+ code => sub { push @calls, {@_} },
+ ],
+ ]
+ );
+
+ $log->error('foo');
+ $log->info('bar');
+ $log->critical('baz');
+
+ is_deeply(
+ \@calls,
+ [
+ {
+ level => 'error',
+ message => 'foo',
+ }, {
+ level => 'critical',
+ message => 'baz',
+ },
+ ],
+ 'code received the expected messages'
+ );
+}
+
+done_testing();
+
+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};
+}
+
+# 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, @_ );
+}
+
+#line 10000
+package Croaker;
+
+sub croak {
+ my $log = shift;
+
+ $log->log_and_croak( level => 'error', message => 'croak' );
+}
diff --git a/t/02-email-exit.t b/t/02-email-exit.t
new file mode 100644
index 0000000..056b52e
--- /dev/null
+++ b/t/02-email-exit.t
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+unless ( -d '.git' ) {
+ plan skip_all => 'This test only runs for the maintainer';
+ exit;
+}
+
+system( $^X, 't/email-exit-helper.pl' );
+
+is( $? >> 8, 5, 'exit code of helper was 5' );
+
+done_testing();
diff --git a/t/03-short-syntax.t b/t/03-short-syntax.t
new file mode 100644
index 0000000..2b972bd
--- /dev/null
+++ b/t/03-short-syntax.t
@@ -0,0 +1,77 @@
+use strict;
+use warnings;
+use lib qw(t/lib);
+use Test::More;
+use Log::Dispatch;
+use Log::Dispatch::TestUtil qw(cmp_deeply);
+use File::Temp qw( tempdir );
+
+my $tempdir = tempdir( CLEANUP => 1 );
+
+{
+ my $emerg_log = File::Spec->catdir( $tempdir, 'emerg.log' );
+
+ # Short syntax
+ my $dispatch0 = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File', name => 'file', min_level => 'emerg',
+ filename => $emerg_log
+ ],
+ [
+ '+Log::Dispatch::Screen', name => 'screen',
+ min_level => 'debug'
+ ]
+ ]
+ );
+
+ # Short syntax alternate (2.23)
+ my $dispatch1 = Log::Dispatch->new(
+ outputs => [
+ 'File' => {
+ name => 'file', min_level => 'emerg', filename => $emerg_log
+ },
+ '+Log::Dispatch::Screen' =>
+ { name => 'screen', min_level => 'debug' }
+ ]
+ );
+
+ # Long syntax
+ my $dispatch2 = Log::Dispatch->new;
+ $dispatch2->add(
+ Log::Dispatch::File->new(
+ name => 'file',
+ min_level => 'emerg',
+ filename => $emerg_log
+ )
+ );
+ $dispatch2->add(
+ Log::Dispatch::Screen->new( name => 'screen', min_level => 'debug' )
+ );
+
+ cmp_deeply(
+ $dispatch0, $dispatch2,
+ "created equivalent dispatchers - 0"
+ );
+ cmp_deeply(
+ $dispatch1, $dispatch2,
+ "created equivalent dispatchers - 1"
+ );
+}
+
+{
+ eval { Log::Dispatch->new( outputs => ['File'] ) };
+ like(
+ $@, qr/expected arrayref/,
+ "got error for expected inner arrayref"
+ );
+}
+{
+ eval { Log::Dispatch->new( outputs => 'File' ) };
+ like(
+ $@, qr/not one of the allowed types: arrayref/,
+ "got error for expected outer arrayref"
+ );
+}
+
+done_testing();
diff --git a/t/04-binmode.t b/t/04-binmode.t
new file mode 100644
index 0000000..b5ac1fd
--- /dev/null
+++ b/t/04-binmode.t
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Test::More 0.88;
+
+use Log::Dispatch;
+use Log::Dispatch::File;
+
+plan skip_all => "Cannot test utf8 files with this version of Perl ($])"
+ unless $] >= 5.008;
+
+my $dir = tempdir( CLEANUP => 1 );
+
+my %params = (
+ name => 'file',
+ min_level => 'debug',
+ filename => File::Spec->catfile( $dir, 'logfile_X.txt' ),
+);
+
+my @tests = (
+ {
+ params => { %params, 'binmode' => ':utf8' },
+ message => "foo bar\x{20AC}",
+ expected_message => "foo bar\xe2\x82\xac",
+ },
+);
+
+my $count = 0;
+for my $t (@tests) {
+ my $dispatcher = Log::Dispatch->new();
+ ok( $dispatcher, 'got a logger object' );
+
+ $t->{params}{filename} =~ s/X\.txt$/$count++ . '.txt'/e;
+ my $file = $t->{params}{filename};
+
+ my $logger = Log::Dispatch::File->new( %{ $t->{params} } );
+ ok( $logger, 'got a file output object' );
+
+ $dispatcher->add($logger);
+ $dispatcher->log( level => 'info', message => $t->{message} );
+
+ ok( -e $file, "$file exists" );
+ open my $fh, '<', $file;
+
+ my $line = do { local $/; <$fh> };
+ close $fh;
+
+ is( $line, $t->{expected_message}, 'output contains UTF-8 bytes' );
+}
+
+done_testing();
+
diff --git a/t/05-close-after-write.t b/t/05-close-after-write.t
new file mode 100644
index 0000000..7875e8a
--- /dev/null
+++ b/t/05-close-after-write.t
@@ -0,0 +1,97 @@
+use strict;
+use warnings FATAL => 'all';
+
+use Test::More 0.88;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Log::Dispatch;
+
+my $dir = tempdir( CLEANUP => 1 );
+
+# test that the same handle is returned if close-on-write is not set...
+
+{
+ my $logger = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File',
+ min_level => 'debug',
+ newline => 1,
+ name => 'no_caw',
+ filename => File::Spec->catfile( $dir, 'no_caw.log' ),
+ close_after_write => 0,
+ ],
+ [
+ 'File',
+ min_level => 'debug',
+ newline => 1,
+ name => 'caw',
+ filename => File::Spec->catfile( $dir, 'caw.log' ),
+ close_after_write => 1,
+ ],
+ ],
+ );
+
+ ok(
+ $logger->output('no_caw')->{fh},
+ 'no_caw output has created a fh before first write'
+ );
+ ok(
+ !$logger->output('caw')->{fh},
+ 'caw output has not created a fh before first write'
+ );
+
+ $logger->log( level => 'info', message => 'first message' );
+ is(
+ _slurp( $logger->output('no_caw')->{filename} ),
+ "first message\n",
+ 'first line from no_caw output'
+ );
+ is(
+ _slurp( $logger->output('caw')->{filename} ),
+ "first message\n",
+ 'first line from caw output'
+ );
+
+ my %handle = (
+ no_caw => $logger->output('no_caw')->{fh},
+ caw => $logger->output('caw')->{fh},
+ );
+
+ $logger->log( level => 'info', message => 'second message' );
+
+ is(
+ _slurp( $logger->output('no_caw')->{filename} ),
+ "first message\nsecond message\n",
+ 'full content from no_caw output'
+ );
+ is(
+ _slurp( $logger->output('caw')->{filename} ),
+ "first message\nsecond message\n",
+ 'full content from caw output'
+ );
+
+ # check the filehandles again...
+ is(
+ $logger->output('no_caw')->{fh},
+ $handle{no_caw},
+ 'handle has not changed when not using CAW'
+ );
+ isnt(
+ $logger->output('caw')->{fh},
+ $handle{caw},
+ 'handle has changed when using CAW'
+ );
+}
+
+done_testing();
+
+sub _slurp {
+ open my $fh, '<', $_[0]
+ or die "Cannot read $_[0]: $!";
+ return do {
+ local $/;
+ <$fh>;
+ };
+}
diff --git a/t/06-syslog.t b/t/06-syslog.t
new file mode 100644
index 0000000..7ae6eb2
--- /dev/null
+++ b/t/06-syslog.t
@@ -0,0 +1,66 @@
+use strict;
+use warnings;
+
+use Test::More 0.88;
+
+use Test::Requires {
+ 'Sys::Syslog' => '0.28',
+};
+
+use Log::Dispatch;
+use Log::Dispatch::Syslog;
+
+no warnings 'redefine', 'once';
+
+my @sock;
+local *Sys::Syslog::setlogsock = sub { @sock = @_ };
+
+local *Sys::Syslog::openlog = sub { return 1 };
+local *Sys::Syslog::closelog = sub { return 1 };
+
+my @log;
+local *Sys::Syslog::syslog = sub { push @log, [@_] };
+
+{
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::Syslog->new(
+ name => 'syslog',
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->info('Foo');
+
+ ok(
+ !@sock,
+ 'no call to setlogsock unless socket is set explicitly'
+ );
+
+ is_deeply(
+ \@log,
+ [ [ 'INFO', 'Foo' ] ],
+ 'passed message to syslog'
+ );
+}
+
+{
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::Syslog->new(
+ name => 'syslog',
+ min_level => 'debug',
+ socket => { type => 'foo' },
+ )
+ );
+
+ $dispatch->info('Foo');
+
+ is_deeply(
+ \@sock,
+ [ { type => 'foo' } ],
+ 'call to setlogsock is made when logging a message if socket was passed to LD::Syslog constructor'
+ );
+}
+
+done_testing();
diff --git a/t/author-eol.t b/t/author-eol.t
new file mode 100644
index 0000000..8a36b70
--- /dev/null
+++ b/t/author-eol.t
@@ -0,0 +1,60 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.18
+
+use Test::More 0.88;
+use Test::EOL;
+
+my @files = (
+ 'lib/Log/Dispatch.pm',
+ 'lib/Log/Dispatch/ApacheLog.pm',
+ 'lib/Log/Dispatch/Base.pm',
+ 'lib/Log/Dispatch/Code.pm',
+ 'lib/Log/Dispatch/Conflicts.pm',
+ 'lib/Log/Dispatch/Email.pm',
+ 'lib/Log/Dispatch/Email/MIMELite.pm',
+ 'lib/Log/Dispatch/Email/MailSend.pm',
+ 'lib/Log/Dispatch/Email/MailSender.pm',
+ 'lib/Log/Dispatch/Email/MailSendmail.pm',
+ 'lib/Log/Dispatch/File.pm',
+ 'lib/Log/Dispatch/File/Locked.pm',
+ 'lib/Log/Dispatch/Handle.pm',
+ 'lib/Log/Dispatch/Null.pm',
+ 'lib/Log/Dispatch/Output.pm',
+ 'lib/Log/Dispatch/Screen.pm',
+ 'lib/Log/Dispatch/Syslog.pm',
+ 't/00-compile.t',
+ 't/00-report-prereqs.dd',
+ 't/00-report-prereqs.t',
+ 't/01-basic.t',
+ 't/02-email-exit.t',
+ 't/03-short-syntax.t',
+ 't/04-binmode.t',
+ 't/05-close-after-write.t',
+ 't/06-syslog.t',
+ 't/author-eol.t',
+ 't/author-no-tabs.t',
+ 't/author-pod-spell.t',
+ 't/email-exit-helper.pl',
+ 't/lib/Log/Dispatch/TestUtil.pm',
+ 't/release-cpan-changes.t',
+ 't/release-pod-coverage.t',
+ 't/release-pod-no404s.t',
+ 't/release-pod-syntax.t',
+ 't/release-portability.t',
+ 't/release-test-version.t',
+ 't/release-tidyall.t',
+ 't/sendmail'
+);
+
+eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
+done_testing;
diff --git a/t/author-no-tabs.t b/t/author-no-tabs.t
new file mode 100644
index 0000000..6c3dc9c
--- /dev/null
+++ b/t/author-no-tabs.t
@@ -0,0 +1,60 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15
+
+use Test::More 0.88;
+use Test::NoTabs;
+
+my @files = (
+ 'lib/Log/Dispatch.pm',
+ 'lib/Log/Dispatch/ApacheLog.pm',
+ 'lib/Log/Dispatch/Base.pm',
+ 'lib/Log/Dispatch/Code.pm',
+ 'lib/Log/Dispatch/Conflicts.pm',
+ 'lib/Log/Dispatch/Email.pm',
+ 'lib/Log/Dispatch/Email/MIMELite.pm',
+ 'lib/Log/Dispatch/Email/MailSend.pm',
+ 'lib/Log/Dispatch/Email/MailSender.pm',
+ 'lib/Log/Dispatch/Email/MailSendmail.pm',
+ 'lib/Log/Dispatch/File.pm',
+ 'lib/Log/Dispatch/File/Locked.pm',
+ 'lib/Log/Dispatch/Handle.pm',
+ 'lib/Log/Dispatch/Null.pm',
+ 'lib/Log/Dispatch/Output.pm',
+ 'lib/Log/Dispatch/Screen.pm',
+ 'lib/Log/Dispatch/Syslog.pm',
+ 't/00-compile.t',
+ 't/00-report-prereqs.dd',
+ 't/00-report-prereqs.t',
+ 't/01-basic.t',
+ 't/02-email-exit.t',
+ 't/03-short-syntax.t',
+ 't/04-binmode.t',
+ 't/05-close-after-write.t',
+ 't/06-syslog.t',
+ 't/author-eol.t',
+ 't/author-no-tabs.t',
+ 't/author-pod-spell.t',
+ 't/email-exit-helper.pl',
+ 't/lib/Log/Dispatch/TestUtil.pm',
+ 't/release-cpan-changes.t',
+ 't/release-pod-coverage.t',
+ 't/release-pod-no404s.t',
+ 't/release-pod-syntax.t',
+ 't/release-portability.t',
+ 't/release-test-version.t',
+ 't/release-tidyall.t',
+ 't/sendmail'
+);
+
+notabs_ok($_) foreach @files;
+done_testing;
diff --git a/t/author-pod-spell.t b/t/author-pod-spell.t
new file mode 100644
index 0000000..e17f428
--- /dev/null
+++ b/t/author-pod-spell.t
@@ -0,0 +1,97 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+use Test::More;
+
+# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.006009
+use Test::Spelling 0.12;
+use Pod::Wordlist;
+
+
+add_stopwords(<DATA>);
+all_pod_files_spelling_ok( qw( bin lib ) );
+__DATA__
+DROLSKY
+DROLSKY's
+Rolsky
+Rolsky's
+API
+CPAN
+Cholet
+Dumont
+Goess
+Manfredi
+Miyagawa
+PayPal
+Pfeiffer
+STDERR
+STDOUT
+Schilli
+Straup
+Subclasses
+Swartz
+Tatsuhiko
+UTF
+apache
+appenders
+auth
+authpriv
+autoflushed
+classname
+crit
+emerg
+filename
+ident
+kern
+logopt
+multi
+params
+smtp
+stderr
+subclass's
+subclasses
+uucp
+Dave
+autarch
+Karen
+Etheridge
+ether
+Olaf
+Alders
+olaf
+Olivier
+Mengué
+dolmen
+Ross
+Attrill
+ross
+swartz
+Whitney
+Jackson
+whitney
+lib
+Log
+Dispatch
+ApacheLog
+Base
+Code
+Conflicts
+Email
+MIMELite
+MailSend
+MailSender
+MailSendmail
+File
+Locked
+Handle
+Null
+Output
+Screen
+Syslog
diff --git a/t/email-exit-helper.pl b/t/email-exit-helper.pl
new file mode 100755
index 0000000..1ffe5c5
--- /dev/null
+++ b/t/email-exit-helper.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use lib './lib', '../lib';
+
+use Log::Dispatch::Email::MailSend;
+
+Mail::Mailer->import( sendmail => 't/sendmail' );
+
+my $email = Log::Dispatch::Email::MailSend->new(
+ name => 'email',
+ min_level => 'emerg',
+ to => 'foo@example.com',
+ subject => 'Log this',
+);
+
+$email->log( message => 'Something bad is happening', level => 'emerg' );
+
+exit 5;
diff --git a/t/lib/Log/Dispatch/TestUtil.pm b/t/lib/Log/Dispatch/TestUtil.pm
new file mode 100644
index 0000000..5d9f5ab
--- /dev/null
+++ b/t/lib/Log/Dispatch/TestUtil.pm
@@ -0,0 +1,44 @@
+package Log::Dispatch::TestUtil;
+use Data::Dumper;
+use strict;
+use warnings;
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+ cmp_deeply
+ dump_one_line
+);
+
+sub cmp_deeply {
+ my ( $ref1, $ref2, $name ) = @_;
+
+ my $tb = Test::Builder->new();
+ $tb->is_eq( dump_one_line($ref1), dump_one_line($ref2), $name );
+}
+
+sub dump_one_line {
+ my ($value) = @_;
+
+ return Data::Dumper->new( [$value] )->Indent(0)->Sortkeys(1)
+ ->Quotekeys(0)->Terse(1)->Dump();
+}
+
+1;
+
+# ABSTRACT: Utilities used internally by Log::Dispatch for testing
+
+__END__
+
+=head1 METHODS
+
+=over
+
+=item cmp_deeply
+
+A cheap version of Test::Deep::cmp_deeply.
+
+=item dump_one_line
+
+Dump a value to a single line using Data::Dumper.
+
+=cut
diff --git a/t/release-cpan-changes.t b/t/release-cpan-changes.t
new file mode 100644
index 0000000..214650f
--- /dev/null
+++ b/t/release-cpan-changes.t
@@ -0,0 +1,19 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+
+use Test::More 0.96 tests => 2;
+use_ok('Test::CPAN::Changes');
+subtest 'changes_ok' => sub {
+ changes_file_ok('Changes');
+};
+done_testing();
diff --git a/t/release-pod-coverage.t b/t/release-pod-coverage.t
new file mode 100644
index 0000000..55bb523
--- /dev/null
+++ b/t/release-pod-coverage.t
@@ -0,0 +1,63 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable.
+
+use Test::Pod::Coverage 1.08;
+use Test::More 0.88;
+
+BEGIN {
+ if ( $] <= 5.008008 ) {
+ plan skip_all => 'These tests require Pod::Coverage::TrustPod, which only works with Perl 5.8.9+';
+ }
+}
+use Pod::Coverage::TrustPod;
+
+my %skip = map { $_ => 1 } qw( Log::Dispatch::ApacheLog Log::Dispatch::Conflicts );
+
+my @modules;
+for my $module ( all_modules() ) {
+ next if $skip{$module};
+
+ push @modules, $module;
+}
+
+plan skip_all => 'All the modules we found were excluded from POD coverage test.'
+ unless @modules;
+
+plan tests => scalar @modules;
+
+my %trustme = (
+ 'Log::Dispatch::File' => [
+ qr/^(?:O_)?APPEND$/
+ ],
+ 'Log::Dispatch::Output' => [
+ qr/^new$/
+ ],
+ 'Log::Dispatch' => [
+ qr/^(?:warn|err|crit|emerg)$/,
+ qr/^is_\w+$/
+ ]
+ );
+
+my @also_private;
+
+for my $module ( sort @modules ) {
+ pod_coverage_ok(
+ $module,
+ {
+ coverage_class => 'Pod::Coverage::TrustPod',
+ also_private => \@also_private,
+ trustme => $trustme{$module} || [],
+ },
+ "pod coverage for $module"
+ );
+}
+
+done_testing();
diff --git a/t/release-pod-no404s.t b/t/release-pod-no404s.t
new file mode 100644
index 0000000..da185ec
--- /dev/null
+++ b/t/release-pod-no404s.t
@@ -0,0 +1,29 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+use Test::More;
+
+foreach my $env_skip ( qw(
+ SKIP_POD_NO404S
+ AUTOMATED_TESTING
+) ){
+ plan skip_all => "\$ENV{$env_skip} is set, skipping"
+ if $ENV{$env_skip};
+}
+
+eval "use Test::Pod::No404s";
+if ( $@ ) {
+ plan skip_all => 'Test::Pod::No404s required for testing POD';
+}
+else {
+ all_pod_files_ok();
+}
diff --git a/t/release-pod-syntax.t b/t/release-pod-syntax.t
new file mode 100644
index 0000000..cdd6a6c
--- /dev/null
+++ b/t/release-pod-syntax.t
@@ -0,0 +1,14 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
+use Test::More;
+use Test::Pod 1.41;
+
+all_pod_files_ok();
diff --git a/t/release-portability.t b/t/release-portability.t
new file mode 100644
index 0000000..ad285b4
--- /dev/null
+++ b/t/release-portability.t
@@ -0,0 +1,20 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+
+use Test::More;
+
+eval 'use Test::Portability::Files';
+plan skip_all => 'Test::Portability::Files required for testing portability'
+ if $@;
+
+run_tests();
diff --git a/t/release-test-version.t b/t/release-test-version.t
new file mode 100644
index 0000000..aef8636
--- /dev/null
+++ b/t/release-test-version.t
@@ -0,0 +1,46 @@
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+use strict;
+use warnings;
+use Test::More;
+
+# generated by Dist::Zilla::Plugin::Test::Version 1.03
+use Test::Version;
+
+my @imports = qw( version_ok );
+
+my $params = {
+ is_strict => 0,
+ has_version => 1,
+
+};
+
+push @imports, $params
+ if version->parse( $Test::Version::VERSION ) >= version->parse('1.002');
+
+
+Test::Version->import(@imports);
+
+version_ok('lib/Log/Dispatch.pm');
+version_ok('lib/Log/Dispatch/ApacheLog.pm');
+version_ok('lib/Log/Dispatch/Base.pm');
+version_ok('lib/Log/Dispatch/Code.pm');
+version_ok('lib/Log/Dispatch/Email.pm');
+version_ok('lib/Log/Dispatch/Email/MIMELite.pm');
+version_ok('lib/Log/Dispatch/Email/MailSend.pm');
+version_ok('lib/Log/Dispatch/Email/MailSender.pm');
+version_ok('lib/Log/Dispatch/Email/MailSendmail.pm');
+version_ok('lib/Log/Dispatch/File.pm');
+version_ok('lib/Log/Dispatch/File/Locked.pm');
+version_ok('lib/Log/Dispatch/Handle.pm');
+version_ok('lib/Log/Dispatch/Null.pm');
+version_ok('lib/Log/Dispatch/Output.pm');
+version_ok('lib/Log/Dispatch/Screen.pm');
+version_ok('lib/Log/Dispatch/Syslog.pm');
+done_testing;
diff --git a/t/release-tidyall.t b/t/release-tidyall.t
new file mode 100644
index 0000000..9625e53
--- /dev/null
+++ b/t/release-tidyall.t
@@ -0,0 +1,17 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::Test::TidyAll
+
+use Test::Code::TidyAll 0.24;
+use Test::More 0.88;
+
+tidyall_ok();
+
+done_testing();
diff --git a/t/sendmail b/t/sendmail
new file mode 100755
index 0000000..d41087f
--- /dev/null
+++ b/t/sendmail
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exit 0;