]> git.wincent.com - wikitext.git/commitdiff
Add sanity checks to parsing benchmark scripts
authorWincent Colaiuta <win@wincent.com>
Sun, 10 May 2009 23:44:11 +0000 (01:44 +0200)
committerWincent Colaiuta <win@wincent.com>
Sun, 10 May 2009 23:44:11 +0000 (01:44 +0200)
After the grand refactoring there are evidently still some lingering
low-level errors, because the benchmarking scripts are bailing with
an "overlong encoding" error after a certain period of time (full
output below).

I've added some sanity checks to the scripts to try and catch discrepancies
but so far none have been discovered.

Here is the full output of the run (this one for "parsing.rb", but the
results are similar for "profile_parsing.rb"):

Rehearsal -------------------------------------------------------------
short slab of ASCII text    1.800000   0.020000   1.820000 (  2.182344)
short slab of UTF-8 text    3.540000   0.030000   3.570000 (  4.127638)
longer slab of ASCII text  14.600000   0.140000  14.740000 ( 17.301072)
longer slab of UTF-8 text  46.150000   0.490000  46.640000 ( 58.118039)
--------------------------------------------------- total: 66.770000sec

user     system      total        real
short slab of ASCII text    1.800000   0.020000   1.820000 (  2.087143)
short slab of UTF-8 text    3.580000   0.040000   3.620000 (  4.315676)
longer slab of ASCII text  14.680000   0.160000  14.840000 ( 18.018380)
longer slab of UTF-8 text benchmarks/parsing.rb:321:in `parse': invalid
  encoding: overlong encoding (Wikitext::Parser::Error)
  from benchmarks/parsing.rb:321:in `parse'
  from benchmarks/parsing.rb:320:in `times'
  from benchmarks/parsing.rb:320:in `parse'
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/...
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/...
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/...
  from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/...
  from benchmarks/parsing.rb:331

Signed-off-by: Wincent Colaiuta <win@wincent.com>
benchmarks/parsing.rb
benchmarks/profile_parsing.rb

index 79a292d9d68b1112c029e5d0491f5ffe5ae33caf..86083933fe7ca6d912a4f3c4e568aeeee5f268fc 100755 (executable)
@@ -28,9 +28,22 @@ require 'benchmark'
 # 91 characters, 91 bytes
 short_slab_of_ASCII_text  = '* Lorem [[ipsum|hello]] dolor sit amet, `consectetuer` http://example.com/ adipiscing elit.'
 
+# compare against the expected output to ensure correctness
+short_slab_of_ASCII_text_output = <<SLAB
+<ul>
+  <li>Lorem <a href="/wiki/ipsum">hello</a> dolor sit amet, <tt>consectetuer</tt> <a href="http://example.com/" class="external">http://example.com/</a> adipiscing elit.</li>
+</ul>
+SLAB
+
 # 91 characters, 122 bytes
 short_slab_of_UTF8_text   = '* Lór€m [[ïpsûm|h€llö]] dólór sït àm€t, `cóñs€ct€tû€r` http://example.com/ àdïpïscïñg €lït.'
 
+short_slab_of_UTF8_text_output = <<SLAB
+<ul>
+  <li>L&#x00f3;r&#x20ac;m <a href="/wiki/%c3%afps%c3%bbm">h&#x20ac;ll&#x00f6;</a> d&#x00f3;l&#x00f3;r s&#x00ef;t &#x00e0;m&#x20ac;t, <tt>c&#x00f3;&#x00f1;s&#x20ac;ct&#x20ac;t&#x00fb;&#x20ac;r</tt> <a href="http://example.com/" class="external">http://example.com/</a> &#x00e0;d&#x00ef;p&#x00ef;sc&#x00ef;&#x00f1;g &#x20ac;l&#x00ef;t.</li>
+</ul>
+SLAB
+
 # 1415 characters, 1415 bytes
 longer_slab_of_ASCII_text = <<SLAB
 paragraph
@@ -97,6 +110,76 @@ link. And [http://example.com/ is another.
 # new list
 SLAB
 
+longer_slab_of_ASCII_text_output = <<SLAB
+<p>paragraph second line</p>
+<p>new paragraph</p>
+<h1>a heading</h1>
+<blockquote>
+  <p>a blockquote second line of blockquote</p>
+  <p>new paragraph within blockquote</p>
+</blockquote>
+<h2>another heading</h2>
+<p>paragraph within <em>multiple <strong>styles</strong></em> and <tt>tt span</tt></p>
+<p>similar, but with <strong>styles in <em>different</em> order</strong></p>
+<p>again, a <strong>different <em>order</em></strong></p>
+<ul>
+  <li>list item 1
+    <ul>
+      <li>nested list item 1</li>
+      <li>nested list item 2</li>
+      <li>nested list item 3</li>
+    </ul>
+  </li>
+  <li>list item 2</li>
+</ul>
+<pre>// this is a code block
+notice how it can contain ''markup''
+which would '''otherwise''' have &lt;tt&gt;special&lt;/tt&gt; meaning
+although explicit entities &copy; are passed through unchanged</pre>
+<p>a normal paragraph again</p>
+<p>This is where we show a link to an article on <a href="/wiki/GCC">GCC</a>. Related to that, <a href="/wiki/GCC">a link</a> to the same article but with custom link text.</p>
+<p>External links <a href="http://example.com" class="external">work too</a>. As well as autolinks as seen <a href="http://example.com/" class="external">http://example.com/</a> here.</p>
+<p>Look at how we handle bad syntax. [[This is an unterminated link. And [<a href="http://example.com/" class="external">http://example.com/</a> is another.</p>
+<ol>
+  <li>this is an ordered list</li>
+  <li>which continues
+    <ol>
+      <li>and has another ordered list</li>
+      <li>nested inside it</li>
+    </ol>
+  </li>
+  <li>and then falls back
+    <ul>
+      <li>and then nests another list</li>
+      <li>this time an unordered one
+        <ul>
+          <li>itself containing a nested list</li>
+          <li>which continues
+            <ol>
+              <li>and finally nests yet another ordered list</li>
+              <li>which continues</li>
+            </ol>
+          </li>
+        </ul>
+      </li>
+      <li>drops back quite a way</li>
+    </ul>
+  </li>
+  <li>and finally all the way
+    <ul>
+      <li>***** and finishes with an invalid item</li>
+    </ul>
+  </li>
+</ol>
+<h3>heading with missing closing tag</h3>
+<ul>
+  <li>list</li>
+</ul>
+<ol>
+  <li>new list</li>
+</ol>
+SLAB
+
 # 1415 characters, 2061 bytes
 longer_slab_of_UTF8_text  = <<SLAB
 pärägräph
@@ -163,16 +246,92 @@ lîñk. Añd [http://example.com/ î∫ äñöth€r.
 # ñ€w lî∫t
 SLAB
 
-def parse job, description, parser, input
+longer_slab_of_UTF8_text_output = <<SLAB
+<p>p&#x00e4;r&#x00e4;gr&#x00e4;ph &#x222b;&#x20ac;c&#x00f6;&#x00f1;d l&#x00ee;&#x00f1;&#x20ac;</p>
+<p>&#x00f1;&#x20ac;w p&#x00e4;r&#x00e4;gr&#x00e4;ph</p>
+<h1>&#x00e4; h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g</h1>
+<blockquote>
+  <p>&#x00e4; bl&#x00f6;ckqu&#x00f6;t&#x20ac; &#x222b;&#x20ac;c&#x00f6;&#x00f1;d l&#x00ee;&#x00f1;&#x20ac; &#x00f6;f bl&#x00f6;ckqu&#x00f6;t&#x20ac;</p>
+  <p>&#x00f1;&#x20ac;w p&#x00e4;r&#x00e4;gr&#x00e4;ph w&#x00ee;th&#x00ee;&#x00f1; bl&#x00f6;ckqu&#x00f6;t&#x20ac;</p>
+</blockquote>
+<h2>&#x00e4;&#x00f1;&#x00f6;th&#x20ac;r h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g</h2>
+<p>p&#x00e4;r&#x00e4;gr&#x00e4;ph w&#x00ee;th&#x00ee;&#x00f1; <em>mult&#x00ee;pl&#x20ac; <strong>&#x222b;tyl&#x20ac;&#x222b;</strong></em> &#x00e4;&#x00f1;d <tt>tt &#x222b;p&#x00e4;&#x00f1;</tt></p>
+<p>&#x222b;&#x00ee;m&#x00ee;l&#x00e4;r, but w&#x00ee;th <strong>&#x222b;tyl&#x20ac;&#x222b; &#x00ee;&#x00f1; <em>d&#x00ee;ff&#x20ac;r&#x20ac;&#x00f1;t</em> &#x00f6;rd&#x20ac;r</strong></p>
+<p>&#x00e4;g&#x00e4;&#x00ee;&#x00f1;, &#x00e4; <strong>d&#x00ee;ff&#x20ac;r&#x20ac;&#x00f1;t <em>&#x00f6;rd&#x20ac;r</em></strong></p>
+<ul>
+  <li>l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 1
+    <ul>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 1</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 2</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 3</li>
+    </ul>
+  </li>
+  <li>l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 2</li>
+</ul>
+<pre>// th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4; c&#x00f6;d&#x20ac; bl&#x00f6;ck
+&#x00f1;&#x00f6;t&#x00ee;c&#x20ac; h&#x00f6;w &#x00ee;t c&#x00e4;&#x00f1; c&#x00f6;&#x00f1;t&#x00e4;&#x00ee;&#x00f1; ''m&#x00e4;rkup''
+wh&#x00ee;ch w&#x00f6;uld '''&#x00f6;th&#x20ac;rw&#x00ee;&#x222b;&#x20ac;''' h&#x00e4;v&#x20ac; &lt;tt&gt;&#x222b;p&#x20ac;c&#x00ee;&#x00e4;l&lt;/tt&gt; m&#x20ac;&#x00e4;&#x00f1;&#x00ee;&#x00f1;g
+&#x00e4;lth&#x00f6;ugh &#x20ac;xpl&#x00ee;c&#x00ee;t &#x20ac;&#x00f1;t&#x00ee;t&#x00ee;&#x20ac;&#x222b; &amp;c&#x00f6;py; &#x00e4;r&#x20ac; p&#x00e4;&#x222b;&#x222b;&#x20ac;d thr&#x00f6;ugh u&#x00f1;ch&#x00e4;&#x00f1;g&#x20ac;d</pre>
+<p>&#x00e4; &#x00f1;&#x00f6;rm&#x00e4;l p&#x00e4;r&#x00e4;gr&#x00e4;ph &#x00e4;g&#x00e4;&#x00ee;&#x00f1;</p>
+<p>Th&#x00ee;&#x222b; &#x00ee;&#x222b; wh&#x20ac;r&#x20ac; w&#x20ac; &#x222b;h&#x00f6;w &#x00e4; l&#x00ee;&#x00f1;k t&#x00f6; &#x00e4;&#x00f1; &#x00e4;rt&#x00ee;cl&#x20ac; &#x00f6;&#x00f1; <a href="/wiki/GCC">GCC</a>. R&#x20ac;l&#x00e4;t&#x20ac;d t&#x00f6; th&#x00e4;t, <a href="/wiki/GCC">&#x00e4; l&#x00ee;&#x00f1;k</a> t&#x00f6; th&#x20ac; &#x222b;&#x00e4;m&#x20ac; &#x00e4;rt&#x00ee;cl&#x20ac; but w&#x00ee;th cu&#x222b;t&#x00f6;m l&#x00ee;&#x00f1;k t&#x20ac;xt.</p>
+<p>Ext&#x20ac;r&#x00f1;&#x00e4;l l&#x00ee;&#x00f1;k&#x222b; <a href="http://example.com" class="external">w&#x00f6;rk t&#x00f6;&#x00f6;</a>. A&#x222b; w&#x20ac;ll &#x00e4;&#x222b; &#x00e4;ut&#x00f6;l&#x00ee;&#x00f1;k&#x222b; &#x00e4;&#x222b; &#x222b;&#x20ac;&#x20ac;&#x00f1; <a href="http://example.com/" class="external">http://example.com/</a> her&#x20ac;.</p>
+<p>L&#x00f6;&#x00f6;k &#x00e4;t h&#x00f6;w w&#x20ac; h&#x00e4;&#x00f1;dl&#x20ac; b&#x00e4;d &#x222b;y&#x00f1;t&#x00e4;x. [[Th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4;&#x00f1; u&#x00f1;t&#x20ac;rm&#x00ee;&#x00f1;&#x00e4;t&#x20ac;d l&#x00ee;&#x00f1;k. A&#x00f1;d [<a href="http://example.com/" class="external">http://example.com/</a> &#x00ee;&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r.</p>
+<ol>
+  <li>th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4;&#x00f1; &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+  <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;
+    <ol>
+      <li>&#x00e4;&#x00f1;d h&#x00e4;&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d &#x00ee;&#x00f1;&#x222b;&#x00ee;d&#x20ac; &#x00ee;t</li>
+    </ol>
+  </li>
+  <li>&#x00e4;&#x00f1;d th&#x20ac;&#x00f1; f&#x00e4;ll&#x222b; b&#x00e4;ck
+    <ul>
+      <li>&#x00e4;&#x00f1;d th&#x20ac;&#x00f1; &#x00f1;&#x20ac;&#x222b;t&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r l&#x00ee;&#x222b;t</li>
+      <li>th&#x00ee;&#x222b; t&#x00ee;m&#x20ac; &#x00e4;&#x00f1; u&#x00f1;&#x00f6;rd&#x20ac;r&#x20ac;d &#x00f6;&#x00f1;&#x20ac;
+        <ul>
+          <li>&#x00ee;t&#x222b;&#x20ac;lf c&#x00f6;&#x00f1;t&#x00e4;&#x00ee;&#x00f1;&#x00ee;&#x00f1;g &#x00e4; &#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t</li>
+          <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;
+            <ol>
+              <li>&#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00e4;lly &#x00f1;&#x20ac;&#x222b;t&#x222b; y&#x20ac;t &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+              <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;</li>
+            </ol>
+          </li>
+        </ul>
+      </li>
+      <li>dr&#x00f6;p&#x222b; b&#x00e4;ck qu&#x00ee;t&#x20ac; &#x00e4; w&#x00e4;y</li>
+    </ul>
+  </li>
+  <li>&#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00e4;lly &#x00e4;ll th&#x20ac; w&#x00e4;y
+    <ul>
+      <li>***** &#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00ee;&#x222b;h&#x20ac;&#x222b; w&#x00ee;th &#x00e4;&#x00f1; &#x00ee;&#x00f1;v&#x00e4;l&#x00ee;d &#x00ee;t&#x20ac;m</li>
+    </ul>
+  </li>
+</ol>
+<h3>h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g w&#x00ee;th m&#x00ee;&#x222b;&#x222b;&#x00ee;&#x00f1;g cl&#x00f6;&#x222b;&#x00ee;&#x00f1;g t&#x00e4;g</h3>
+<ul>
+  <li>l&#x00ee;&#x222b;t</li>
+</ul>
+<ol>
+  <li>&#x00f1;&#x20ac;w l&#x00ee;&#x222b;t</li>
+</ol>
+SLAB
+
+def parse job, description, parser, input, expected
   job.report(description) do
-    100_000.times { parser.parse input }
+    100_000.times {
+      output = parser.parse input
+      if output != expected
+        puts output
+        raise 'mismatch'
+      end
+    }
   end
 end
 
 parser = Wikitext::Parser.new
 Benchmark.bmbm do |job|
-  parse job, 'short slab of ASCII text', parser, short_slab_of_ASCII_text
-  parse job, 'short slab of UTF-8 text', parser, short_slab_of_UTF8_text
-  parse job, 'longer slab of ASCII text', parser, longer_slab_of_ASCII_text
-  parse job, 'longer slab of UTF-8 text', parser, longer_slab_of_UTF8_text
+  parse job, 'short slab of ASCII text', parser, short_slab_of_ASCII_text, short_slab_of_ASCII_text_output
+  parse job, 'short slab of UTF-8 text', parser, short_slab_of_UTF8_text, short_slab_of_UTF8_text_output
+  parse job, 'longer slab of ASCII text', parser, longer_slab_of_ASCII_text, longer_slab_of_ASCII_text_output
+  parse job, 'longer slab of UTF-8 text', parser, longer_slab_of_UTF8_text, longer_slab_of_UTF8_text_output
 end
index 2cb33e078eb017eaa8873d8ae6276d8019876b8d..7d2c678c00c4e01479f85368099c8cfb5952dd0a 100755 (executable)
@@ -28,9 +28,22 @@ require 'benchmark'
 # 91 characters, 91 bytes
 short_slab_of_ASCII_text  = '* Lorem [[ipsum|hello]] dolor sit amet, `consectetuer` http://example.com/ adipiscing elit.'
 
+# compare against the expected output to ensure correctness
+short_slab_of_ASCII_text_output = <<SLAB
+<ul>
+  <li>Lorem <a href="/wiki/ipsum">hello</a> dolor sit amet, <tt>consectetuer</tt> <a href="http://example.com/" class="external">http://example.com/</a> adipiscing elit.</li>
+</ul>
+SLAB
+
 # 91 characters, 122 bytes
 short_slab_of_UTF8_text   = '* Lór€m [[ïpsûm|h€llö]] dólór sït àm€t, `cóñs€ct€tû€r` http://example.com/ àdïpïscïñg €lït.'
 
+short_slab_of_UTF8_text_output = <<SLAB
+<ul>
+  <li>L&#x00f3;r&#x20ac;m <a href="/wiki/%c3%afps%c3%bbm">h&#x20ac;ll&#x00f6;</a> d&#x00f3;l&#x00f3;r s&#x00ef;t &#x00e0;m&#x20ac;t, <tt>c&#x00f3;&#x00f1;s&#x20ac;ct&#x20ac;t&#x00fb;&#x20ac;r</tt> <a href="http://example.com/" class="external">http://example.com/</a> &#x00e0;d&#x00ef;p&#x00ef;sc&#x00ef;&#x00f1;g &#x20ac;l&#x00ef;t.</li>
+</ul>
+SLAB
+
 # 1415 characters, 1415 bytes
 longer_slab_of_ASCII_text = <<SLAB
 paragraph
@@ -97,6 +110,76 @@ link. And [http://example.com/ is another.
 # new list
 SLAB
 
+longer_slab_of_ASCII_text_output = <<SLAB
+<p>paragraph second line</p>
+<p>new paragraph</p>
+<h1>a heading</h1>
+<blockquote>
+  <p>a blockquote second line of blockquote</p>
+  <p>new paragraph within blockquote</p>
+</blockquote>
+<h2>another heading</h2>
+<p>paragraph within <em>multiple <strong>styles</strong></em> and <tt>tt span</tt></p>
+<p>similar, but with <strong>styles in <em>different</em> order</strong></p>
+<p>again, a <strong>different <em>order</em></strong></p>
+<ul>
+  <li>list item 1
+    <ul>
+      <li>nested list item 1</li>
+      <li>nested list item 2</li>
+      <li>nested list item 3</li>
+    </ul>
+  </li>
+  <li>list item 2</li>
+</ul>
+<pre>// this is a code block
+notice how it can contain ''markup''
+which would '''otherwise''' have &lt;tt&gt;special&lt;/tt&gt; meaning
+although explicit entities &copy; are passed through unchanged</pre>
+<p>a normal paragraph again</p>
+<p>This is where we show a link to an article on <a href="/wiki/GCC">GCC</a>. Related to that, <a href="/wiki/GCC">a link</a> to the same article but with custom link text.</p>
+<p>External links <a href="http://example.com" class="external">work too</a>. As well as autolinks as seen <a href="http://example.com/" class="external">http://example.com/</a> here.</p>
+<p>Look at how we handle bad syntax. [[This is an unterminated link. And [<a href="http://example.com/" class="external">http://example.com/</a> is another.</p>
+<ol>
+  <li>this is an ordered list</li>
+  <li>which continues
+    <ol>
+      <li>and has another ordered list</li>
+      <li>nested inside it</li>
+    </ol>
+  </li>
+  <li>and then falls back
+    <ul>
+      <li>and then nests another list</li>
+      <li>this time an unordered one
+        <ul>
+          <li>itself containing a nested list</li>
+          <li>which continues
+            <ol>
+              <li>and finally nests yet another ordered list</li>
+              <li>which continues</li>
+            </ol>
+          </li>
+        </ul>
+      </li>
+      <li>drops back quite a way</li>
+    </ul>
+  </li>
+  <li>and finally all the way
+    <ul>
+      <li>***** and finishes with an invalid item</li>
+    </ul>
+  </li>
+</ol>
+<h3>heading with missing closing tag</h3>
+<ul>
+  <li>list</li>
+</ul>
+<ol>
+  <li>new list</li>
+</ol>
+SLAB
+
 # 1415 characters, 2061 bytes
 longer_slab_of_UTF8_text  = <<SLAB
 pärägräph
@@ -163,6 +246,76 @@ lîñk. Añd [http://example.com/ î∫ äñöth€r.
 # ñ€w lî∫t
 SLAB
 
+longer_slab_of_UTF8_text_output = <<SLAB
+<p>p&#x00e4;r&#x00e4;gr&#x00e4;ph &#x222b;&#x20ac;c&#x00f6;&#x00f1;d l&#x00ee;&#x00f1;&#x20ac;</p>
+<p>&#x00f1;&#x20ac;w p&#x00e4;r&#x00e4;gr&#x00e4;ph</p>
+<h1>&#x00e4; h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g</h1>
+<blockquote>
+  <p>&#x00e4; bl&#x00f6;ckqu&#x00f6;t&#x20ac; &#x222b;&#x20ac;c&#x00f6;&#x00f1;d l&#x00ee;&#x00f1;&#x20ac; &#x00f6;f bl&#x00f6;ckqu&#x00f6;t&#x20ac;</p>
+  <p>&#x00f1;&#x20ac;w p&#x00e4;r&#x00e4;gr&#x00e4;ph w&#x00ee;th&#x00ee;&#x00f1; bl&#x00f6;ckqu&#x00f6;t&#x20ac;</p>
+</blockquote>
+<h2>&#x00e4;&#x00f1;&#x00f6;th&#x20ac;r h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g</h2>
+<p>p&#x00e4;r&#x00e4;gr&#x00e4;ph w&#x00ee;th&#x00ee;&#x00f1; <em>mult&#x00ee;pl&#x20ac; <strong>&#x222b;tyl&#x20ac;&#x222b;</strong></em> &#x00e4;&#x00f1;d <tt>tt &#x222b;p&#x00e4;&#x00f1;</tt></p>
+<p>&#x222b;&#x00ee;m&#x00ee;l&#x00e4;r, but w&#x00ee;th <strong>&#x222b;tyl&#x20ac;&#x222b; &#x00ee;&#x00f1; <em>d&#x00ee;ff&#x20ac;r&#x20ac;&#x00f1;t</em> &#x00f6;rd&#x20ac;r</strong></p>
+<p>&#x00e4;g&#x00e4;&#x00ee;&#x00f1;, &#x00e4; <strong>d&#x00ee;ff&#x20ac;r&#x20ac;&#x00f1;t <em>&#x00f6;rd&#x20ac;r</em></strong></p>
+<ul>
+  <li>l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 1
+    <ul>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 1</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 2</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 3</li>
+    </ul>
+  </li>
+  <li>l&#x00ee;&#x222b;t &#x00ee;t&#x20ac;m 2</li>
+</ul>
+<pre>// th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4; c&#x00f6;d&#x20ac; bl&#x00f6;ck
+&#x00f1;&#x00f6;t&#x00ee;c&#x20ac; h&#x00f6;w &#x00ee;t c&#x00e4;&#x00f1; c&#x00f6;&#x00f1;t&#x00e4;&#x00ee;&#x00f1; ''m&#x00e4;rkup''
+wh&#x00ee;ch w&#x00f6;uld '''&#x00f6;th&#x20ac;rw&#x00ee;&#x222b;&#x20ac;''' h&#x00e4;v&#x20ac; &lt;tt&gt;&#x222b;p&#x20ac;c&#x00ee;&#x00e4;l&lt;/tt&gt; m&#x20ac;&#x00e4;&#x00f1;&#x00ee;&#x00f1;g
+&#x00e4;lth&#x00f6;ugh &#x20ac;xpl&#x00ee;c&#x00ee;t &#x20ac;&#x00f1;t&#x00ee;t&#x00ee;&#x20ac;&#x222b; &amp;c&#x00f6;py; &#x00e4;r&#x20ac; p&#x00e4;&#x222b;&#x222b;&#x20ac;d thr&#x00f6;ugh u&#x00f1;ch&#x00e4;&#x00f1;g&#x20ac;d</pre>
+<p>&#x00e4; &#x00f1;&#x00f6;rm&#x00e4;l p&#x00e4;r&#x00e4;gr&#x00e4;ph &#x00e4;g&#x00e4;&#x00ee;&#x00f1;</p>
+<p>Th&#x00ee;&#x222b; &#x00ee;&#x222b; wh&#x20ac;r&#x20ac; w&#x20ac; &#x222b;h&#x00f6;w &#x00e4; l&#x00ee;&#x00f1;k t&#x00f6; &#x00e4;&#x00f1; &#x00e4;rt&#x00ee;cl&#x20ac; &#x00f6;&#x00f1; <a href="/wiki/GCC">GCC</a>. R&#x20ac;l&#x00e4;t&#x20ac;d t&#x00f6; th&#x00e4;t, <a href="/wiki/GCC">&#x00e4; l&#x00ee;&#x00f1;k</a> t&#x00f6; th&#x20ac; &#x222b;&#x00e4;m&#x20ac; &#x00e4;rt&#x00ee;cl&#x20ac; but w&#x00ee;th cu&#x222b;t&#x00f6;m l&#x00ee;&#x00f1;k t&#x20ac;xt.</p>
+<p>Ext&#x20ac;r&#x00f1;&#x00e4;l l&#x00ee;&#x00f1;k&#x222b; <a href="http://example.com" class="external">w&#x00f6;rk t&#x00f6;&#x00f6;</a>. A&#x222b; w&#x20ac;ll &#x00e4;&#x222b; &#x00e4;ut&#x00f6;l&#x00ee;&#x00f1;k&#x222b; &#x00e4;&#x222b; &#x222b;&#x20ac;&#x20ac;&#x00f1; <a href="http://example.com/" class="external">http://example.com/</a> her&#x20ac;.</p>
+<p>L&#x00f6;&#x00f6;k &#x00e4;t h&#x00f6;w w&#x20ac; h&#x00e4;&#x00f1;dl&#x20ac; b&#x00e4;d &#x222b;y&#x00f1;t&#x00e4;x. [[Th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4;&#x00f1; u&#x00f1;t&#x20ac;rm&#x00ee;&#x00f1;&#x00e4;t&#x20ac;d l&#x00ee;&#x00f1;k. A&#x00f1;d [<a href="http://example.com/" class="external">http://example.com/</a> &#x00ee;&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r.</p>
+<ol>
+  <li>th&#x00ee;&#x222b; &#x00ee;&#x222b; &#x00e4;&#x00f1; &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+  <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;
+    <ol>
+      <li>&#x00e4;&#x00f1;d h&#x00e4;&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+      <li>&#x00f1;&#x20ac;&#x222b;t&#x20ac;d &#x00ee;&#x00f1;&#x222b;&#x00ee;d&#x20ac; &#x00ee;t</li>
+    </ol>
+  </li>
+  <li>&#x00e4;&#x00f1;d th&#x20ac;&#x00f1; f&#x00e4;ll&#x222b; b&#x00e4;ck
+    <ul>
+      <li>&#x00e4;&#x00f1;d th&#x20ac;&#x00f1; &#x00f1;&#x20ac;&#x222b;t&#x222b; &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r l&#x00ee;&#x222b;t</li>
+      <li>th&#x00ee;&#x222b; t&#x00ee;m&#x20ac; &#x00e4;&#x00f1; u&#x00f1;&#x00f6;rd&#x20ac;r&#x20ac;d &#x00f6;&#x00f1;&#x20ac;
+        <ul>
+          <li>&#x00ee;t&#x222b;&#x20ac;lf c&#x00f6;&#x00f1;t&#x00e4;&#x00ee;&#x00f1;&#x00ee;&#x00f1;g &#x00e4; &#x00f1;&#x20ac;&#x222b;t&#x20ac;d l&#x00ee;&#x222b;t</li>
+          <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;
+            <ol>
+              <li>&#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00e4;lly &#x00f1;&#x20ac;&#x222b;t&#x222b; y&#x20ac;t &#x00e4;&#x00f1;&#x00f6;th&#x20ac;r &#x00f6;rd&#x20ac;r&#x20ac;d l&#x00ee;&#x222b;t</li>
+              <li>wh&#x00ee;ch c&#x00f6;&#x00f1;t&#x00ee;&#x00f1;u&#x20ac;&#x222b;</li>
+            </ol>
+          </li>
+        </ul>
+      </li>
+      <li>dr&#x00f6;p&#x222b; b&#x00e4;ck qu&#x00ee;t&#x20ac; &#x00e4; w&#x00e4;y</li>
+    </ul>
+  </li>
+  <li>&#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00e4;lly &#x00e4;ll th&#x20ac; w&#x00e4;y
+    <ul>
+      <li>***** &#x00e4;&#x00f1;d f&#x00ee;&#x00f1;&#x00ee;&#x222b;h&#x20ac;&#x222b; w&#x00ee;th &#x00e4;&#x00f1; &#x00ee;&#x00f1;v&#x00e4;l&#x00ee;d &#x00ee;t&#x20ac;m</li>
+    </ul>
+  </li>
+</ol>
+<h3>h&#x20ac;&#x00e4;d&#x00ee;&#x00f1;g w&#x00ee;th m&#x00ee;&#x222b;&#x222b;&#x00ee;&#x00f1;g cl&#x00f6;&#x222b;&#x00ee;&#x00f1;g t&#x00e4;g</h3>
+<ul>
+  <li>l&#x00ee;&#x222b;t</li>
+</ul>
+<ol>
+  <li>&#x00f1;&#x20ac;w l&#x00ee;&#x222b;t</li>
+</ol>
+SLAB
+
 def parse job, description, parser, input
   job.report(description) do
     parser.profiling_parse input
@@ -170,6 +323,13 @@ def parse job, description, parser, input
 end
 
 parser = Wikitext::Parser.new
+
+# sanity check
+raise 'mismatch (short slab of ASCII text)' unless (parser.parse(short_slab_of_ASCII_text) == short_slab_of_ASCII_text_output)
+raise 'mismatch (short slab of UTF-8 text)' unless (parser.parse(short_slab_of_UTF8_text) == short_slab_of_UTF8_text_output)
+raise 'mismatch (longer slab of ASCII text)' unless (parser.parse(longer_slab_of_ASCII_text) == longer_slab_of_ASCII_text_output)
+raise 'mismatch (longer slab of UTF-8 text)' unless (parser.parse(longer_slab_of_UTF8_text) == longer_slab_of_UTF8_text_output)
+
 Benchmark.bmbm do |job|
   parse job, 'short slab of ASCII text', parser, short_slab_of_ASCII_text
   parse job, 'short slab of UTF-8 text', parser, short_slab_of_UTF8_text