| 1 | package Test::Snippet; |
|---|
| 2 | use strict; |
|---|
| 3 | use warnings; |
|---|
| 4 | use 5.00800; |
|---|
| 5 | our $VERSION = '0.01'; |
|---|
| 6 | use base qw/Test::Builder::Module/; |
|---|
| 7 | use Text::Diff qw(diff); |
|---|
| 8 | use Data::Dumper; |
|---|
| 9 | |
|---|
| 10 | our @EXPORT = qw/test_snippet test_snippet_in_pod/; |
|---|
| 11 | |
|---|
| 12 | my $CLASS = __PACKAGE__; |
|---|
| 13 | |
|---|
| 14 | our $Dumper = \&Data::Dumper::Dumper; |
|---|
| 15 | our $Driver; |
|---|
| 16 | |
|---|
| 17 | sub test_snippet { |
|---|
| 18 | my $test = shift; |
|---|
| 19 | |
|---|
| 20 | unless ($Driver) { |
|---|
| 21 | eval "use Test::Snippet::Driver::DevelREPL;"; ## no critic |
|---|
| 22 | die $@ if $@; |
|---|
| 23 | $Driver = Test::Snippet::Driver::DevelREPL->new(); |
|---|
| 24 | } |
|---|
| 25 | |
|---|
| 26 | my ($got, $expected) = $Driver->run($test); |
|---|
| 27 | |
|---|
| 28 | my $got_dumped = $Dumper->($got); |
|---|
| 29 | my $expected_dumped = $Dumper->($expected); |
|---|
| 30 | my $diff = diff(\$got_dumped, \$expected_dumped); |
|---|
| 31 | $CLASS->builder->ok($got_dumped eq $expected_dumped); |
|---|
| 32 | if ($diff) { |
|---|
| 33 | $CLASS->builder->diag($diff); |
|---|
| 34 | } |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | sub test_snippet_in_pod { |
|---|
| 38 | my $pod = shift; |
|---|
| 39 | require Pod::POM; |
|---|
| 40 | |
|---|
| 41 | my $parser = Pod::POM->new(); |
|---|
| 42 | my $pom = $parser->parse($pod) || die $parser->error; |
|---|
| 43 | |
|---|
| 44 | my $traverse; |
|---|
| 45 | $traverse = sub { |
|---|
| 46 | my $c = shift; |
|---|
| 47 | for my $c ($c->content) { |
|---|
| 48 | if ($c->type eq 'text') { |
|---|
| 49 | # nop. |
|---|
| 50 | } elsif (($c->type eq 'begin' || $c->type eq 'for') && $c->format eq 'test') { |
|---|
| 51 | # do it |
|---|
| 52 | test_snippet( $c->content ); |
|---|
| 53 | } else { |
|---|
| 54 | $traverse->($c); # recurse. |
|---|
| 55 | } |
|---|
| 56 | } |
|---|
| 57 | }; |
|---|
| 58 | |
|---|
| 59 | $traverse->($pom); |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | 1; |
|---|
| 63 | __END__ |
|---|
| 64 | |
|---|
| 65 | =for stopwords doctest API |
|---|
| 66 | |
|---|
| 67 | =encoding utf8 |
|---|
| 68 | |
|---|
| 69 | =head1 NAME |
|---|
| 70 | |
|---|
| 71 | Test::Snippet - doctest for perl |
|---|
| 72 | |
|---|
| 73 | =head1 SYNOPSIS |
|---|
| 74 | |
|---|
| 75 | use Test::Snippet tests => 1; |
|---|
| 76 | |
|---|
| 77 | # simple repl: |
|---|
| 78 | test_snippet(<<'...'); |
|---|
| 79 | $ 3+2 |
|---|
| 80 | 5 |
|---|
| 81 | ... |
|---|
| 82 | |
|---|
| 83 | # tests in pod: |
|---|
| 84 | test_snippet_in_pod(<<'...'); |
|---|
| 85 | =head1 DESCRIPTION |
|---|
| 86 | |
|---|
| 87 | ... |
|---|
| 88 | |
|---|
| 89 | =begin test |
|---|
| 90 | |
|---|
| 91 | $ 4*5 |
|---|
| 92 | 20 |
|---|
| 93 | |
|---|
| 94 | =end |
|---|
| 95 | ... |
|---|
| 96 | |
|---|
| 97 | =head1 DESCRIPTION |
|---|
| 98 | |
|---|
| 99 | Test::Snippet is doctest for perl. |
|---|
| 100 | |
|---|
| 101 | THIS MODULE IS IN ITS BETA QUALITY. API MAY CHANGE IN THE FUTURE. |
|---|
| 102 | |
|---|
| 103 | =head1 FAQ |
|---|
| 104 | |
|---|
| 105 | =over 4 |
|---|
| 106 | |
|---|
| 107 | =item How does this compare to Test::Inline, or Test::Pod::Snippets? |
|---|
| 108 | |
|---|
| 109 | Very similar. |
|---|
| 110 | |
|---|
| 111 | But, Test::Snippet way is based on REPL(read eval print loop). |
|---|
| 112 | This is very readable and users can run in own console! |
|---|
| 113 | |
|---|
| 114 | =back |
|---|
| 115 | |
|---|
| 116 | =head1 AUTHOR |
|---|
| 117 | |
|---|
| 118 | Tokuhiro Matsuno E<lt>tokuhirom@gmail.comE<gt> |
|---|
| 119 | |
|---|
| 120 | =head1 SEE ALSO |
|---|
| 121 | |
|---|
| 122 | L<Test::Pod::Snippets>, L<Test::Inline> |
|---|
| 123 | |
|---|
| 124 | =head1 LICENSE |
|---|
| 125 | |
|---|
| 126 | This library is free software; you can redistribute it and/or modify |
|---|
| 127 | it under the same terms as Perl itself. |
|---|
| 128 | |
|---|
| 129 | =cut |
|---|