use strict;
use Test::More;
use HTML::Tiny;

# We have 100% coverage without these tests. Consider these extra
# security against something getting twisted out of shape.

my %schedule;

BEGIN {
    my @common_schedule = (
        {
            "expect_list" => [
                "<a href=\"http://hexten.net\">Hexten</a>",
                "<a href=\"http://search.cpan.org\">CPAN Search</a>"
            ],
            "args" => [
                { "href" => "http://hexten.net" },
                "Hexten",
                { "href" => "http://search.cpan.org" },
                "CPAN Search"
            ],
            "expect_scalar" =>
              "<a href=\"http://hexten.net\">Hexten</a><a href=\"http://search.cpan.org\">CPAN Search</a>",
            "method" => "a"
        },
        {
            "expect_list" => [ "<abbr>one</abbr>", "<abbr>two</abbr>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<abbr>one</abbr><abbr>two</abbr>",
            "method"        => "abbr"
        },
        {
            "expect_list" =>
              [ "<acronym>one</acronym>", "<acronym>two</acronym>" ],
            "args" => [ {}, "one", { x => 1 }, { x => undef }, "two" ],
            "expect_scalar" =>
              "<acronym>one</acronym><acronym>two</acronym>",
            "method" => "acronym"
        },
        {
            "expect_list" => [
                "<address>onetwo</address>",
                "<address>threefour</address>"
            ],
            "args" => [ [ "one", "two" ], [ "three", "four" ] ],
            "expect_scalar" =>
              "<address>onetwo</address><address>threefour</address>",
            "method" => "address"
        },
        {
            "expect_list"   => ["<one>two</one>"],
            "args"          => [ "one", "two" ],
            "expect_scalar" => "<one>two</one>",
            "method"        => "auto_tag"
        },
        {
            "expect_list"   => [ "<b>one</b>", "<b>two</b>" ],
            "args"          => [ "one",        "two" ],
            "expect_scalar" => "<b>one</b><b>two</b>",
            "method"        => "b"
        },
        {
            "expect_list" => [ "<bdo>one</bdo>", "<bdo>two</bdo>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<bdo>one</bdo><bdo>two</bdo>",
            "method"        => "bdo"
        },
        {
            "expect_list" => [ "<big>one</big>", "<big>two</big>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<big>one</big><big>two</big>",
            "method"        => "big"
        },
        {
            "expect_list" => [
                "<blockquote>one</blockquote>",
                "<blockquote>two</blockquote>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<blockquote>one</blockquote><blockquote>two</blockquote>",
            "method" => "blockquote"
        },
        {
            "expect_list" =>
              [ "<body>one</body>\n", "<body>two</body>\n" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<body>one</body>\n<body>two</body>\n",
            "method"        => "body"
        },
        {
            "expect_list" =>
              [ "<button>one</button>", "<button>two</button>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<button>one</button><button>two</button>",
            "method" => "button"
        },
        {
            "expect_list" =>
              [ "<caption>one</caption>", "<caption>two</caption>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<caption>one</caption><caption>two</caption>",
            "method" => "caption"
        },
        {
            "expect_list" => [ "<cite>one</cite>", "<cite>two</cite>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<cite>one</cite><cite>two</cite>",
            "method"        => "cite"
        },
        {
            "expect_list"   => ["</tag>"],
            "args"          => ["tag"],
            "expect_scalar" => "</tag>",
            "method"        => "close"
        },
        {
            "expect_list" => [ "<code>one</code>", "<code>two</code>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<code>one</code><code>two</code>",
            "method"        => "code"
        },
        {
            "expect_list" => [
                "<colgroup>one</colgroup>", "<colgroup>two</colgroup>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<colgroup>one</colgroup><colgroup>two</colgroup>",
            "method" => "colgroup"
        },
        {
            "expect_list" => [ "<dd>one</dd>", "<dd>two</dd>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<dd>one</dd><dd>two</dd>",
            "method"        => "dd"
        },
        {
            "expect_list" => [ "<del>one</del>", "<del>two</del>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<del>one</del><del>two</del>",
            "method"        => "del"
        },
        {
            "expect_list" => [ "<dfn>one</dfn>", "<dfn>two</dfn>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<dfn>one</dfn><dfn>two</dfn>",
            "method"        => "dfn"
        },
        {
            "expect_list" => [ "<div>one</div>\n", "<div>two</div>\n" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<div>one</div>\n<div>two</div>\n",
            "method"        => "div"
        },
        {
            "expect_list" => [ "<dl>one</dl>", "<dl>two</dl>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<dl>one</dl><dl>two</dl>",
            "method"        => "dl"
        },
        {
            "expect_list" => [ "<dt>one</dt>", "<dt>two</dt>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<dt>one</dt><dt>two</dt>",
            "method"        => "dt"
        },
        {
            "expect_list" => [ "<em>one</em>", "<em>two</em>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<em>one</em><em>two</em>",
            "method"        => "em"
        },
        {
            "expect_list"   => ["one"],
            "args"          => [ "one", "two" ],
            "expect_scalar" => "one",
            "method"        => "entity_encode"
        },
        {
            "expect_list" => [
                "<fieldset>one</fieldset>", "<fieldset>two</fieldset>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<fieldset>one</fieldset><fieldset>two</fieldset>",
            "method" => "fieldset"
        },
        {
            "expect_list" => [ "<form>one</form>", "<form>two</form>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<form>one</form><form>two</form>",
            "method"        => "form"
        },
        {
            "expect_list" => [
                "<frameset>one</frameset>", "<frameset>two</frameset>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<frameset>one</frameset><frameset>two</frameset>",
            "method" => "frameset"
        },
        {
            "expect_list" => [ "<h1>one</h1>", "<h1>two</h1>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h1>one</h1><h1>two</h1>",
            "method"        => "h1"
        },
        {
            "expect_list" => [ "<h2>one</h2>", "<h2>two</h2>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h2>one</h2><h2>two</h2>",
            "method"        => "h2"
        },
        {
            "expect_list" => [ "<h3>one</h3>", "<h3>two</h3>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h3>one</h3><h3>two</h3>",
            "method"        => "h3"
        },
        {
            "expect_list" => [ "<h4>one</h4>", "<h4>two</h4>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h4>one</h4><h4>two</h4>",
            "method"        => "h4"
        },
        {
            "expect_list" => [ "<h5>one</h5>", "<h5>two</h5>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h5>one</h5><h5>two</h5>",
            "method"        => "h5"
        },
        {
            "expect_list" => [ "<h6>one</h6>", "<h6>two</h6>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<h6>one</h6><h6>two</h6>",
            "method"        => "h6"
        },
        {
            "expect_list" =>
              [ "<head>one</head>\n", "<head>two</head>\n" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<head>one</head>\n<head>two</head>\n",
            "method"        => "head"
        },
        {
            "expect_list" =>
              [ "<html>one</html>\n", "<html>two</html>\n" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<html>one</html>\n<html>two</html>\n",
            "method"        => "html"
        },
        {
            "expect_list"   => [ "<i>one</i>", "<i>two</i>" ],
            "args"          => [ "one",        "two" ],
            "expect_scalar" => "<i>one</i><i>two</i>",
            "method"        => "i"
        },
        {
            "expect_list" =>
              [ "<iframe>one</iframe>", "<iframe>two</iframe>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<iframe>one</iframe><iframe>two</iframe>",
            "method" => "iframe"
        },
        {
            "expect_list" => [ "<ins>one</ins>", "<ins>two</ins>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<ins>one</ins><ins>two</ins>",
            "method"        => "ins"
        },
        {
            "expect_list"   => ["[{},{},null,{\"x\":1}]"],
            "args"          => [ [ {}, {}, undef, { x => 1 } ] ],
            "expect_scalar" => "[{},{},null,{\"x\":1}]",
            "method"        => "json_encode"
        },
        {
            "expect_list" => [ "<kbd>one</kbd>", "<kbd>two</kbd>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<kbd>one</kbd><kbd>two</kbd>",
            "method"        => "kbd"
        },
        {
            "expect_list" =>
              [ "<label>one</label>", "<label>two</label>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<label>one</label><label>two</label>",
            "method"        => "label"
        },
        {
            "expect_list" =>
              [ "<legend>one</legend>", "<legend>two</legend>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<legend>one</legend><legend>two</legend>",
            "method" => "legend"
        },
        {
            "expect_list" => [ "<li>one</li>", "<li>two</li>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<li>one</li><li>two</li>",
            "method"        => "li"
        },
        {
            "expect_list" => [ "<link>one</link>", "<link>two</link>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<link>one</link><link>two</link>",
            "method"        => "link"
        },
        {
            "expect_list" => [ "<map>one</map>", "<map>two</map>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<map>one</map><map>two</map>",
            "method"        => "map"
        },
        {
            "expect_list" => [
                "<noframes>one</noframes>", "<noframes>two</noframes>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<noframes>one</noframes><noframes>two</noframes>",
            "method" => "noframes"
        },
        {
            "expect_list" => [
                "<noscript>one</noscript>", "<noscript>two</noscript>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<noscript>one</noscript><noscript>two</noscript>",
            "method" => "noscript"
        },
        {
            "expect_list" =>
              [ "<object>one</object>", "<object>two</object>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<object>one</object><object>two</object>",
            "method" => "object"
        },
        {
            "expect_list" => [ "<ol>one</ol>", "<ol>two</ol>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<ol>one</ol><ol>two</ol>",
            "method"        => "ol"
        },
        {
            "args"          => ['pie'],
            "expect_scalar" => "<pie>",
            "method"        => "open"
        },
        {
            "expect_list" => [
                "<optgroup>one</optgroup>", "<optgroup>two</optgroup>"
            ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<optgroup>one</optgroup><optgroup>two</optgroup>",
            "method" => "optgroup"
        },
        {
            "expect_list" =>
              [ "<option>one</option>", "<option>two</option>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<option>one</option><option>two</option>",
            "method" => "option"
        },
        {
            "expect_list" => [ "<p>one</p>\n", "<p>two</p>\n" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<p>one</p>\n<p>two</p>\n",
            "method"        => "p"
        },
        {
            "expect_list" => [ "<pre>one</pre>", "<pre>two</pre>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<pre>one</pre><pre>two</pre>",
            "method"        => "pre"
        },
        {
            "expect_list"   => [ "<q>one</q>", "<q>two</q>" ],
            "args"          => [ "one",        "two" ],
            "expect_scalar" => "<q>one</q><q>two</q>",
            "method"        => "q"
        },
        {
            "args" => [ { spaces => '   ', '&' => '>' } ],
            "expect_scalar" => "%26=%3e&spaces=+++",
            "method"        => "query_encode"
        },
        {
            "expect_list" => [ "<samp>one</samp>", "<samp>two</samp>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<samp>one</samp><samp>two</samp>",
            "method"        => "samp"
        },
        {
            "expect_list" =>
              [ "<script>one</script>", "<script>two</script>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<script>one</script><script>two</script>",
            "method" => "script"
        },
        {
            "expect_list" =>
              [ "<select>one</select>", "<select>two</select>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<select>one</select><select>two</select>",
            "method" => "select"
        },
        {
            "expect_list" =>
              [ "<small>one</small>", "<small>two</small>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<small>one</small><small>two</small>",
            "method"        => "small"
        },
        {
            "expect_list" => [ "<span>one</span>", "<span>two</span>" ],
            "args"        => [ "one",              "two" ],
            "expect_scalar" => "<span>one</span><span>two</span>",
            "method"        => "span"
        },
        {
            "expect_list" =>
              [ "<strong>one</strong>", "<strong>two</strong>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<strong>one</strong><strong>two</strong>",
            "method" => "strong"
        },
        {
            "expect_list" =>
              [ "<style>one</style>", "<style>two</style>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<style>one</style><style>two</style>",
            "method"        => "style"
        },
        {
            "expect_list" => [ "<sub>one</sub>", "<sub>two</sub>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<sub>one</sub><sub>two</sub>",
            "method"        => "sub"
        },
        {
            "expect_list" => [ "<sup>one</sup>", "<sup>two</sup>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<sup>one</sup><sup>two</sup>",
            "method"        => "sup"
        },
        {
            "expect_list" =>
              [ "<table>one</table>\n", "<table>two</table>\n" ],
            "args" => [ "one", "two" ],
            "expect_scalar" =>
              "<table>one</table>\n<table>two</table>\n",
            "method" => "table"
        },
        {
            "expect_list"   => ["<one>two</one>"],
            "args"          => [ "one", "two" ],
            "expect_scalar" => "<one>two</one>",
            "method"        => "tag"
        },
        {
            "expect_list" =>
              [ "<tbody>one</tbody>", "<tbody>two</tbody>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<tbody>one</tbody><tbody>two</tbody>",
            "method"        => "tbody"
        },
        {
            "expect_list" => [ "<td>one</td>", "<td>two</td>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<td>one</td><td>two</td>",
            "method"        => "td"
        },
        {
            "expect_list" => [
                "<textarea>one</textarea>",
                "<textarea cols=\"20\">two</textarea>"
            ],
            "args" => [ "one", { cols => 20 }, "two" ],
            "expect_scalar" =>
              "<textarea>one</textarea><textarea cols=\"20\">two</textarea>",
            "method" => "textarea"
        },
        {
            "expect_list" =>
              [ "<tfoot>one</tfoot>", "<tfoot>two</tfoot>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<tfoot>one</tfoot><tfoot>two</tfoot>",
            "method"        => "tfoot"
        },
        {
            "expect_list" => [ "<th>one</th>", "<th>two</th>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<th>one</th><th>two</th>",
            "method"        => "th"
        },
        {
            "expect_list" =>
              [ "<thead>one</thead>", "<thead>two</thead>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<thead>one</thead><thead>two</thead>",
            "method"        => "thead"
        },
        {
            "expect_list" =>
              [ "<title>one</title>", "<title>two</title>" ],
            "args" => [ "one", "two" ],
            "expect_scalar" => "<title>one</title><title>two</title>",
            "method"        => "title"
        },
        {
            "expect_list" => [ "<tr>one</tr>\n", "<tr>two</tr>\n" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<tr>one</tr>\n<tr>two</tr>\n",
            "method"        => "tr"
        },
        {
            "expect_list" => [ "<tt>one</tt>", "<tt>two</tt>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<tt>one</tt><tt>two</tt>",
            "method"        => "tt"
        },
        {
            "expect_list" => [ "<ul>one</ul>", "<ul>two</ul>" ],
            "args"        => [ "one",          "two" ],
            "expect_scalar" => "<ul>one</ul><ul>two</ul>",
            "method"        => "ul"
        },
        {
            "expect_list"   => ["   !"],
            "args"          => ['++%20%21'],
            "expect_scalar" => "   !",
            "method"        => "url_decode"
        },
        {
            "expect_list"   => ['+++%21'],
            "args"          => ['   !'],
            "expect_scalar" => '+++%21',
            "method"        => "url_encode"
        },
        {
            "expect_list" => [ "<var>one</var>", "<var>two</var>" ],
            "args"        => [ "one",            "two" ],
            "expect_scalar" => "<var>one</var><var>two</var>",
            "method"        => "var"
        }
    );

    my @schedule_xml = (
        @common_schedule,
        {
            "args" => [ { name => 'foo' } ],
            "expect_scalar" => "<area name=\"foo\" />",
            "expect_list"   => ["<area name=\"foo\" />"],
            "method"        => "area"
        },
        {
            "args" => [ { href => 'http://hexten.net/' } ],
            "expect_scalar" => "<base href=\"http://hexten.net/\" />",
            "expect_list"   => ["<base href=\"http://hexten.net/\" />"],
            "method"        => "base"
        },
        {
            "args"          => [],
            "expect_scalar" => "<br />",
            "method"        => "br"
        },
        {
            "args"          => ['frob'],
            "expect_scalar" => "<frob />",
            "method"        => "closed"
        },
        {
            "args"          => [],
            "expect_scalar" => "<col />",
            "method"        => "col"
        },
        {
            "args"          => [],
            "expect_scalar" => "<frame />",
            "method"        => "frame"
        },
        {
            "args"          => [],
            "expect_scalar" => "<hr />",
            "method"        => "hr"
        },
        {
            # This is correct according to our hash merging rules
            "args" =>
              [ { src => 'logo.png' }, { src => 'header.png' } ],
            "expect_list"   => ['<img src="header.png" />'],
            "expect_scalar" => '<img src="header.png" />',
            "method"        => "img"
        },
        {
            "args" => [ { type => 'text' }, { name => 'widget' } ],
            "expect_scalar" =>
              "<input name=\"widget\" type=\"text\" />",
            "method" => "input"
        },
        {
            "args"          => [],
            "expect_scalar" => "<meta />",
            "method"        => "meta"
        },
        {
            "args"          => [ { value => 1 } ],
            "expect_scalar" => "<param value=\"1\" />",
            "method"        => "param"
        },
    );

    my @schedule_html = (
        @common_schedule,
        {
            "args" => [ { name => 'foo' } ],
            "expect_scalar" => "<area name=\"foo\">",
            "expect_list"   => ["<area name=\"foo\">"],
            "method"        => "area"
        },
        {
            "args" => [ { href => 'http://hexten.net/' } ],
            "expect_scalar" => "<base href=\"http://hexten.net/\">",
            "expect_list"   => ["<base href=\"http://hexten.net/\">"],
            "method"        => "base"
        },
        {
            "args"          => [],
            "expect_scalar" => "<br>",
            "method"        => "br"
        },
        {
            "args"          => ['frob'],
            "expect_scalar" => "<frob>",
            "method"        => "closed"
        },
        {
            "args"          => [],
            "expect_scalar" => "<col>",
            "method"        => "col"
        },
        {
            "args"          => [],
            "expect_scalar" => "<frame>",
            "method"        => "frame"
        },
        {
            "args"          => [],
            "expect_scalar" => "<hr>",
            "method"        => "hr"
        },
        {
            # This is correct according to our hash merging rules
            "args" =>
              [ { src => 'logo.png' }, { src => 'header.png' } ],
            "expect_list"   => ['<img src="header.png">'],
            "expect_scalar" => '<img src="header.png">',
            "method"        => "img"
        },
        {
            "args" => [ { type => 'text' }, { name => 'widget' } ],
            "expect_scalar" => "<input name=\"widget\" type=\"text\">",
            "method"        => "input"
        },
        {
            "args"          => [],
            "expect_scalar" => "<meta>",
            "method"        => "meta"
        },
        {
            "args"          => [ { value => 1 } ],
            "expect_scalar" => "<param value=\"1\">",
            "method"        => "param"
        },
    );

    plan tests => ( @schedule_xml + @schedule_html ) * 3 * 4;
    @schedule{qw(xml html)} = ( \@schedule_xml, \@schedule_html );
}

sub apply_test {
    my ( $h, $test ) = @_;

    my $method = $test->{method};
    can_ok $h, $method;

    my $got = $h->$method( @{ $test->{args} } );
    is_deeply $got, $test->{expect_scalar},
      "$method: scalar result matches";

    my $expect_list = $test->{expect_list}
      || [ $test->{expect_scalar} ];
    my @got = $h->$method( @{ $test->{args} } );
    is_deeply \@got, $expect_list, "$method: list result matches";
}

for my $mode ( qw(xml html) ) {
    my @schedule = @{ $schedule{$mode} };

   # Run the tests three times, forwards and backwards to make sure they
   # don't interfere with each other.
    {
        my $h = HTML::Tiny->new( mode => $mode );
        apply_test( $h, $_ )
          for @schedule, reverse @schedule, @schedule;
    }

    # And once again, this time with a fresh HTML::Tiny for each test
    apply_test( HTML::Tiny->new( mode => $mode ), $_ ) for @schedule;
}

