| #!/usr/bin/env perl |
| use warnings; |
| use strict; |
| use Term::ANSIColor; |
| use IO::Handle; |
| use IPC::Open2; |
| |
| my $usage="runtests.pl SPEC PROGRAM\nSet ANSI_COLORS_DISABLED=1 if you redirect to a file.\nSet PATT='...' to restrict tests to sections matching a regex.\n"; |
| |
| my $SPEC = shift @ARGV; |
| my @PROG = @ARGV; |
| my $PATT=$ENV{'PATT'}; |
| |
| if (!(@PROG && defined $SPEC)) { |
| print STDERR $usage; |
| exit 1; |
| } |
| |
| my $passed = 0; |
| my $failed = 0; |
| my $skipped = 0; |
| my $errored = 0; |
| |
| # Markdown implementations vary on insignificant whitespace. |
| # Some leave blanks between block elements, others don't. |
| # This function tries to normalize the output so it can be |
| # compared with our test. tidy takes two arguments: the |
| # string containing the actual output, and a pathname of the |
| # file to which the tidied output is to be saved. |
| sub tidy |
| { |
| my $inpre = 0; |
| my $out = ""; |
| my $outfh; |
| open($outfh, '>', \$out); |
| for (split /^/, $_[0]) { |
| if (/<pre/) { |
| $inpre = 1; |
| } elsif (/<\/pre/) { |
| $inpre = 0; |
| } |
| # remove \r to allow mixing linux/windows newlines |
| s/\r//; |
| if ($inpre) { |
| print $outfh $_; |
| } else { |
| # remove leading spaces |
| s/^ *//; |
| # remove trailing spaces |
| s/ *$//; |
| # collapse consecutive spaces |
| s/ */ /; |
| # collapse space before /> in tag |
| s/ *\/>/\/>/; |
| s/>\n$/>/; |
| # skip blank line |
| if (/^$/) { |
| next; |
| } |
| print $outfh $_; |
| } |
| } |
| close $outfh; |
| return $out; |
| } |
| |
| # return 0 for passing test, -1 for failing, positive for error |
| sub dotest |
| { |
| my $markdown = $_[0]; |
| my $html = $_[1]; |
| my $testname = $_[2]; |
| my $actual = ""; |
| # We use → to indicate tab and ␣ space in the spec |
| $markdown =~ s/→/\t/g;s/␣/ /g; |
| $html =~ s/→/\t/g;s/␣/ /g; |
| my $pid = open2(my $out, my $in, @PROG); |
| print $in $markdown; |
| close $in; |
| flush $out; |
| $actual = do { local $/; <$out>; }; |
| close $out; |
| waitpid($pid, 0); |
| my $exit_status = $?; |
| $html = &tidy($html); |
| $actual = &tidy($actual); |
| $actual =~ s/\'/'/g; |
| |
| if ($actual eq $html) { |
| print colored("✓", "green"); |
| return 0; |
| } else { |
| print colored("\n✘ $testname", "red"); |
| print "\n"; |
| print color "cyan"; |
| print "=== markdown ===============\n"; |
| print $markdown; |
| print "=== expected ===============\n"; |
| print $html; |
| print "\n"; |
| print "=== got ====================\n"; |
| print $actual; |
| print "\n"; |
| print color "black"; |
| if ($exit_status == 0) { |
| return -1; |
| } else { |
| return $exit_status; |
| } |
| } |
| } |
| |
| my $stage = 0; |
| my $markdown = ""; |
| my $html = ""; |
| my $example = 0; |
| my $linenum = 0; |
| my $exampleline = 0; |
| my @secnums = (); |
| my $secheading; |
| my $testresult; |
| |
| open(SPEC, "< $SPEC"); |
| while (<SPEC>) { |
| $linenum++; |
| if (/^\.$/) { |
| $stage = ($stage + 1) % 3; |
| if ($stage == 1) { |
| $exampleline = $linenum; |
| } |
| if ($stage == 0) { |
| $example++; |
| if (!$PATT || $secheading =~ /$PATT/) { |
| $testresult = &dotest($markdown, $html, "Example $example (line $exampleline)"); |
| if ($testresult == 0) { |
| $passed++; |
| } elsif ($testresult == -1) { |
| $failed++; |
| } else { |
| $errored++; |
| } |
| } else { |
| $skipped++; |
| } |
| $markdown = ""; |
| $html = ""; |
| } |
| } elsif ($stage == 0 && $_ =~ /^<!-- END TESTS -->/) { |
| last; |
| } elsif ($stage == 0 && $_ =~ /^(#+) +(.*)/) { |
| my $seclevel = length($1); |
| $secheading = $2; |
| if ($#secnums == $seclevel - 1) { |
| $secnums[$#secnums]++; |
| } elsif ($#secnums > $seclevel - 1) { |
| @secnums = @secnums[0..($seclevel - 1)]; |
| $secnums[$#secnums]++; |
| } else { |
| while ($#secnums < $seclevel - 1) { |
| push(@secnums, 1); |
| } |
| } |
| if (!$PATT || $secheading =~ /$PATT/) { |
| print ("\n", join(".", @secnums) . " " . $secheading, " "); |
| } |
| } elsif ($stage == 1) { |
| $markdown .= $_; |
| } elsif ($stage == 2) { |
| $html .= $_; |
| } |
| } |
| |
| print "\n"; |
| print STDERR colored("$passed tests passed, $failed failed, $errored errored, $skipped skipped.", "bold"); |
| print STDERR "\n"; |
| exit $failed; |