View Javadoc
1   package org.apache.maven.doxia.module.confluence;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.FilterWriter;
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.io.StringReader;
26  import java.io.StringWriter;
27  import java.io.Writer;
28  import java.util.Iterator;
29  
30  import org.apache.maven.doxia.parser.AbstractParserTest;
31  import org.apache.maven.doxia.parser.ParseException;
32  import org.apache.maven.doxia.parser.Parser;
33  import org.apache.maven.doxia.sink.Sink;
34  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
35  import org.apache.maven.doxia.sink.SinkEventElement;
36  import org.apache.maven.doxia.sink.SinkEventTestingSink;
37  import org.apache.maven.doxia.sink.TextSink;
38  
39  import org.codehaus.plexus.util.IOUtil;
40  import org.codehaus.plexus.util.StringUtils;
41  
42  /**
43   * Test class for ConfluenceParser.
44   */
45  public class ConfluenceParserTest
46      extends AbstractParserTest
47  {
48      private ConfluenceParser parser;
49  
50      private StringWriter output;
51  
52      private Reader reader;
53  
54      private Writer writer;
55  
56      /** {@inheritDoc} */
57      protected void setUp()
58          throws Exception
59      {
60          super.setUp();
61  
62          parser = (ConfluenceParser) lookup( Parser.ROLE, "confluence" );
63  
64          output = null;
65          reader = null;
66          writer = null;
67      }
68  
69      /** {@inheritDoc} */
70      protected void tearDown()
71          throws Exception
72      {
73          IOUtil.close( output );
74          IOUtil.close( reader );
75          IOUtil.close( writer );
76  
77          super.tearDown();
78      }
79  
80      /** {@inheritDoc} */
81      protected Parser createParser()
82      {
83          return parser;
84      }
85  
86      /** {@inheritDoc} */
87      protected String outputExtension()
88      {
89          return "confluence";
90      }
91  
92      /** @throws Exception */
93      public void testMarkupTestPage()
94          throws Exception
95      {
96          String result = locateAndParseTestSourceFile( "test" );
97          assertContainsLines( result, "end:body" );
98      }
99  
100     /** @throws Exception */
101     public void testParagraphWithSimpleFormatting()
102         throws Exception
103     {
104         String result = locateAndParseTestSourceFile( "simple-paragraph" );
105 
106         assertContainsLines( result, "begin:bold\ntext: bold\n" );
107         assertContainsLines( result, "begin:italic\ntext: italic\n" );
108         assertContainsLines( result, "begin:monospaced\ntext: monospaced\n" );
109         assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: http://jira.codehaus.org" );
110         assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: JIRA\n" );
111         // four paragraphs in the input...
112         assertEquals( 5, result.split( "end:paragraph" ).length );
113     }
114 
115     /** @throws Exception */
116     public void testLineBreak()
117         throws Exception
118     {
119         String lineBreak = getLineBreakString();
120 
121         String result = locateAndParseTestSourceFile( "linebreak" );
122 
123         assertContainsLines( result, "Line\n" + lineBreak );
124         assertContainsLines( result, "with 2\n" + lineBreak );
125         assertContainsLines( result, "inline\n" + lineBreak );
126     }
127 
128     /** @throws Exception */
129     public void testEscapes()
130         throws Exception
131     {
132         String result = locateAndParseTestSourceFile( "escapes" );
133 
134         assertContainsLines( result, "asterisk *" );
135         assertContainsLines( result, "underline _" );
136         assertContainsLines( result, "asterisk *not bold*" );
137         assertContainsLines( result, "underline _not italic_" );
138         assertContainsLines( result, "normal character" );
139         assertContainsLines( result, "trailing slash\\\n" );
140     }
141 
142     /** @throws Exception */
143     public void testSectionTitles()
144         throws Exception
145     {
146         String result = locateAndParseTestSourceFile( "section" );
147 
148         for ( int i = 1; i <= 5; i++ )
149         {
150             assertContainsLines( "Could not locate section " + i + " title", result,
151                     "sectionTitle" + i + "\ntext: Section" + i );
152         }
153 
154         assertContainsLines( "Section title has leading space", result, "sectionTitle1\ntext: TitleWithLeadingSpace" );
155     }
156 
157     /** @throws Exception */
158     public void testNestedBulletList()
159         throws Exception
160     {
161         String result = locateAndParseTestSourceFile( "nested-list" );
162 
163         assertContainsLines( "Nested list not found", result,
164                 "begin:listItem\ntext: A top level list item\nbegin:list" );
165         // two lists in the input...
166         assertEquals( 3, result.split( "end:list\n" ).length );
167         // ...and 4 list items
168         assertEquals( 5, result.split( "end:listItem\n" ).length );
169     }
170 
171     /** @throws Exception */
172     public void testNestedHeterogenousList()
173         throws Exception
174     {
175     	String result = locateAndParseTestSourceFile( "nested-list-heterogenous" );
176 
177         // test heterogenous list
178         assertContainsLines( "Nested list not found", result, "begin:listItem\ntext: A top level list item\nbegin:numberedList" );
179 
180         // exactly one list and one numberedList
181         assertEquals( 2, result.split( "begin:list\n" ).length );
182         assertEquals( 2, result.split( "begin:numberedList" ).length );
183 
184         // ...and 4 list items
185         assertEquals( 5, result.split( "end:listItem\n" ).length );
186     }
187 
188     /** @throws Exception */
189     public void testListWithSimpleFormatting()
190         throws Exception
191     {
192         String result = locateAndParseTestSourceFile( "simple-list" );
193 
194         assertContainsLines( result, "begin:bold\ntext: bold\n" );
195         assertContainsLines( result, "begin:italic\ntext: italic\n" );
196         assertContainsLines( result, "begin:monospaced\ntext: monospaced\n" );
197         assertContainsLines( result, "begin:monospaced\ntext: some escaped monospaced \\\\unc\\path\n" );
198         assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: http://jira.codehaus.org\n" );
199         assertContainsLines( result, "begin:link, name: http://jira.codehaus.org\ntext: JIRA\n" );
200         assertContainsLines( result, "begin:listItem\ntext: Item with no formatting\nend:listItem\n" );
201         assertContainsLines( result, "begin:listItem\ntext: One bullet\nend:listItem\n" );
202         assertContainsLines( result, "begin:listItem\ntext: A list item with more than one line\nend:listItem\n" );
203         // 3 lists in the input...
204         assertEquals( 4, result.split( "end:list\n" ).length );
205         // ...and 7 list items
206         assertEquals( 9, result.split( "end:listItem\n" ).length );
207     }
208 
209     /** @throws Exception */
210     public void testAnchor()
211         throws Exception
212     {
213         String result = locateAndParseTestSourceFile( "anchor" );
214 
215         assertContainsLines( result, "begin:paragraph\nbegin:anchor, name: start\nend:anchor" );
216         assertContainsLines( result, "begin:anchor, name: middle\nend:anchor" );
217         assertContainsLines( result, "begin:paragraph\ntext: Simple paragraph\nbegin:anchor, name: end\nend:anchor" );
218         // 3 anchors in the input...
219         assertEquals( 4, result.split( "end:anchor\n" ).length );
220     }
221 
222     /** @throws Exception */
223     public void testUnknownMacro()
224         throws Exception
225     {
226         String result = locateAndParseTestSourceFile( "unknown-macro" );
227 
228         assertContainsLines( result, "begin:paragraph\ntext: {unknown:start}" );
229     }
230 
231     /** @throws Exception */
232     public void testCodeMacro()
233         throws Exception
234     {
235         String result = locateAndParseTestSourceFile( "code" );
236 
237         assertContainsLines( result, "begin:verbatim, boxed: true\ntext: public class Cat {" );
238         // 5 paragraphs in the input...
239         assertEquals( 5, result.split( "end:paragraph\n" ).length );
240         // 3 verbatim in the input...
241         assertEquals( 3, result.split( "end:verbatim\n" ).length );
242     }
243 
244     /** @throws Exception */
245     public void testFigure()
246         throws Exception
247     {
248         Reader result = getTestReader( "figure" );
249 
250         SinkEventTestingSink sink = new SinkEventTestingSink();
251 
252         parser.parse( result, sink );
253 
254         Iterator<SinkEventElement> it = sink.getEventList().iterator();
255 
256         assertEquals( it, "head", "head_", "body", "paragraph" );
257         assertEquals( it.next(), "text", "Simple paragraph." );
258         assertEquals( it, "paragraph_", "figure" );
259         assertEquals( it.next(), "figureGraphics", "images/photo.jpg" );
260         assertEquals( it, "figure_", "paragraph" );
261         assertEquals( it.next(), "text", "Simple paragraph with attempted inline !image.jpg! (should fail)." );
262         assertEquals( it, "paragraph_", "figure" );
263         assertEquals( it.next(), "figureGraphics", "images/photo.jpg" );
264         assertEquals( it.next().getName(), "figureCaption" ); 
265         assertEquals( it.next(), "text", "With caption on same line" );
266         assertEquals( it, "figureCaption_", "figure_", "figure" );
267         assertEquals( it.next(), "figureGraphics", "images/linebreak.jpg" );
268         assertEquals( it.next().getName(), "figureCaption" );
269         assertEquals( it.next(), "text", "With caption underneath and linebreak" );
270         assertEquals( it, "figureCaption_", "figure_", "figure" );
271         assertEquals( it.next(), "figureGraphics", "images/nolinebreak.jpg" );
272         assertEquals( it.next().getName(), "figureCaption" );
273         assertEquals( it.next(), "text", "With caption underneath and no linebreak" );
274         assertEquals( it, "figureCaption_", "figure_", "figure" );
275         assertEquals( it.next(), "figureGraphics", "images/bold.jpg" );
276         assertEquals( it.next().getName(), "figureCaption" );
277         assertEquals( it.next(), "text", "With *bold* caption underneath" );
278         assertEquals( it, "figureCaption_", "figure_", "figure" );
279         assertEquals( it.next(), "figureGraphics", "image.gif" );
280         assertEquals( it, "figure_", "body_" );
281         assertFalse( it.hasNext() );
282     }
283 
284     /** @throws Exception */
285     public void testLink()
286         throws Exception
287     {
288         String result = locateAndParseTestSourceFile( "link" );
289 
290         assertContainsLines( result, "begin:link, name: middle.html\ntext: middle\nend:link" );
291         assertContainsLines( result, "begin:link, name: end.html\ntext: end\nend:link" );
292         assertContainsLines( result, "begin:link, name: link.html\ntext: alias\nend:link" );
293         assertContainsLines( result, "begin:link, name: link.html#anchor\ntext: link#anchor\nend:link" );
294         assertContainsLines( result, "begin:link, name: #simple\ntext: simple\nend:link" );
295         assertContainsLines( result, "begin:link, name: resource.pdf\ntext: resource.pdf\nend:link" );
296         assertContainsLines( result, "begin:link, name: resource.pdf\ntext: alias pdf\nend:link" );
297         assertContainsLines( result, "begin:link, name: http://link.to/page_with_underscore-and-dash\ntext: underscore_-dash\nend:link" );
298         // 5 paragraphs in the input...
299         assertEquals( 6, result.split( "end:paragraph\n" ).length );
300         // 8 links in the input...
301         assertEquals( 9, result.split( "end:link\n" ).length );
302     }
303 
304     /** @throws Exception */
305     public void testTableWithLinks()
306         throws Exception
307     {
308         String result = locateAndParseTestSourceFile( "table-link" );
309 
310         assertContainsLines( result, "begin:tableCell\nbegin:link, name: http://example.com/release.0.1.3/ex-win32-win32.x86.zip\ntext: Download\nend:link\n\n\nend:tableCell\n" );
311         assertContainsLines( result, "begin:tableCell\nbegin:link, name: http://example.com/release.0.1.2/ex-win32-win32.x86.zip\ntext: http://example.com/release.0.1.2/ex-win32-win32.x86.zip\nend:link\n\n\nend:tableCell\n" );
312 
313         // 3 links in the input
314         assertEquals( 4, result.split( "end:link\n" ).length );
315     }
316 
317     /** @throws Exception */
318     public void testTableWithImages()
319         throws Exception
320     {
321         // DOXIA-493
322         StringReader reader =
323             new StringReader( "Table containing image in cell:\n" + "\n" + "||Header 1||\n"
324                 + "|!images/test/Image.png!|" );
325 
326         SinkEventTestingSink sink = new SinkEventTestingSink();
327 
328         parser.parse( reader, sink );
329 
330         Iterator<SinkEventElement> it = sink.getEventList().iterator();
331 
332         assertEquals( it, "head", "head_", "body", "paragraph" );
333         assertEquals( it.next(), "text", "Table containing image in cell:" );
334         assertEquals( it, "paragraph_", "table", "tableRows", "tableRow", "tableHeaderCell", "bold" );
335         assertEquals( it.next(), "text", "Header 1" );
336         assertEquals( it, "bold_", "tableHeaderCell_", "tableRow_", "tableRow", "tableCell", "figure" );
337         assertEquals( it.next(), "figureGraphics", "images/test/Image.png" );
338         assertEquals( it, "figure_", "tableCell_", "tableRow_", "tableRows_", "table_", "body_" );
339         assertFalse( it.hasNext() );
340     }
341 
342     /** @throws Exception */
343     public void testParagraphWithList()
344         throws Exception
345     {
346         String result = locateAndParseTestSourceFile( "paragraph-list" );
347 
348         assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
349         assertContainsLines( result, "begin:listItem\ntext: A nested list item\nend:listItem\n" );
350         assertContainsLines( result, "begin:listItem\ntext: Another nested list item with two lines\nend:listItem\n" );
351         // 2 paragraphs in the input...
352         assertEquals( 3, result.split( "end:paragraph\n" ).length );
353         // 1 list in the input...
354         assertEquals( 2, result.split( "end:list\n" ).length );
355     }
356 
357     /** @throws Exception */
358     public void testParagraphWithFigure()
359         throws Exception
360     {
361         String result = locateAndParseTestSourceFile( "paragraph-figure" );
362 
363         assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
364         assertContainsLines( result, "begin:figure\nfigureGraphics, name: images/logo.png\nbegin:figureCaption\ntext: with a figure\nend:figureCaption" );
365         // 2 paragraphs in the input...
366         assertEquals( 3, result.split( "end:paragraph\n" ).length );
367         // 1 figure in the input...
368         assertEquals( 2, result.split( "end:figure\n" ).length );
369     }
370 
371     /** @throws Exception */
372     public void testParagraphWithHeader()
373         throws Exception
374     {
375         String result = locateAndParseTestSourceFile( "paragraph-header" );
376 
377         assertContainsLines( result, "begin:paragraph\ntext: A paragraph\nend:paragraph\n" );
378         assertContainsLines( result, "begin:section2\nbegin:sectionTitle2\ntext: A header\nend:sectionTitle2" );
379         // 3 paragraphs in the input...
380         assertEquals( 4, result.split( "end:paragraph\n" ).length );
381         // 1 header in the input...
382         assertEquals( 2, result.split( "end:sectionTitle2\n" ).length );
383     }
384 
385     /** @throws Exception */
386     public void testNestedFormats()
387         throws Exception
388     {
389         String result = locateAndParseTestSourceFile( "nested-format" );
390 
391         assertContainsLines( result, "begin:bold\nbegin:italic\ntext: bold italic\nend:italic" );
392         assertContainsLines( result, "begin:italic\nbegin:bold\ntext: italic bold\nend:bold" );
393         assertContainsLines( result, "begin:bold\nbegin:monospaced\ntext: bold monospaced\nend:monospaced" );
394         assertContainsLines( result, "text: A paragraph with \nbegin:bold\ntext: bold \nbegin:italic\ntext: italic\nend:italic" );
395         assertContainsLines( result, "begin:italic\ntext: italic \nbegin:bold\ntext: bold\nend:bold" );
396         assertContainsLines( result, "begin:bold\ntext: bold \nbegin:monospaced\ntext: monospaced\nend:monospaced" );
397         // 2 paragraphs in the input...
398         assertEquals( 3, result.split( "end:paragraph\n" ).length );
399         // 6 bolds in the input...
400         assertEquals( 7, result.split( "end:bold\n" ).length );
401         // 4 italics in the input...
402         assertEquals( 5, result.split( "end:italic\n" ).length );
403         // 2 monospaced in the input...
404         assertEquals( 3, result.split( "end:monospaced\n" ).length );
405     }
406 
407     /** @throws Exception */
408     public void testNoteInfoTipQuote()
409         throws Exception
410     {
411         String result = locateAndParseTestSourceFile( "note-tip-info" );
412 
413         assertContainsLines( result, "begin:definedTerm\ntext: Be Careful\nend:definedTerm\n" );
414         assertContainsLines( result, "begin:definition\ntext: The body of the note here..\nend:definition" );
415         assertContainsLines( result, "begin:definedTerm\ntext: Guess What?\nend:definedTerm\n" );
416         assertContainsLines( result, "begin:definition\ntext: The body of the tip here..\nend:definition" );
417         assertContainsLines( result, "begin:definedTerm\ntext: Some Info\nend:definedTerm\n" );
418         assertContainsLines( result, "begin:definition\ntext: The body of the info here..\nend:definition" );
419         assertContainsLines( result, "begin:definedTerm\ntext: Simon Says\nend:definedTerm\n" );
420         assertContainsLines( result, "begin:definition\ntext: The body of the \nbegin:bold\ntext: quote\nend:bold" );
421 
422         // 5 paragraphs in the input...
423         assertEquals( 6, result.split( "end:paragraph\n" ).length );
424         // 4 dinitionList in the input...
425         assertEquals( 5, result.split( "end:definitionList\n" ).length );
426     }
427 
428     /**
429      * DOXIA-247
430      *
431      * @throws ParseException if something goes wrong.
432      */
433     public void testEndBracket()
434         throws ParseException
435     {
436         String document = "Test"
437             + "\n\n* list1"
438             + "\n\n* list2"
439             + "\n\n* list2"
440             + "\n{pre}123{/pre}";
441 
442         output = new StringWriter();
443         Sink sink = new TextSink( output );
444 
445         /* parsing with additional space at end works */
446         createParser().parse( new StringReader( document + " " ), sink );
447         assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
448 
449         /* parsing with document ending in } should not fail */
450         try
451         {
452             createParser().parse( new StringReader( document ), sink );
453         }
454         catch ( Exception e )
455         {
456             e.printStackTrace();
457             fail( "parsing with document ending in } should not fail" );
458         }
459 
460         assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
461     }
462 
463     /**
464      * DOXIA-247
465      *
466      * @throws ParseException
467      */
468     public void testEndBracketInList()
469         throws ParseException
470     {
471         String document1 = "Test"
472             + "\n\n* list1"
473             + "\n\n* list2"
474             + "\n\n* list2{pre}123{/pre} "
475             + "\n123";
476 
477         String document2 = "Test"
478             + "\n\n* list1"
479             + "\n\n* list2"
480             + "\n\n* list2{pre}123{/pre}"
481             + "\n123";
482 
483         output = new StringWriter();
484         Sink sink = new TextSink( output );
485 
486         /* parsing with additional space at end of list item works */
487         createParser().parse( new StringReader( document1 ), sink );
488         assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
489 
490         /* parsing with end of list item ending in } should not fail */
491         try
492         {
493             createParser().parse( new StringReader( document2 ), sink );
494         }
495         catch ( Exception e )
496         {
497             e.printStackTrace();
498             fail( "parsing with end of list item ending in } should not fail" );
499         }
500 
501         assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
502     }
503 
504     public void testDoxia382SinkCannotBeReused()
505             throws ParseException
506     {
507         String document1 = "Test A"
508             + "\n\n* list1"
509             + "\n\n* list2"
510             + "\n\n* list2{pre}123{/pre} "
511             + "\n123";
512 
513         String document2 = "Test B"
514             + "\n\n* list1"
515             + "\n\n* list2"
516             + "\n\n* list2{pre}123{/pre}"
517             + "\n123";
518 
519         output = new StringWriter();
520         Sink sink = new TextSink( new FilterWriter( output )
521         {
522             public void close() throws IOException
523             {
524                 super.close();
525                 this.out = null;
526             }
527 
528             public void write( String str, int off, int len )
529                     throws IOException
530             {
531                 if ( out == null )
532                 {
533                     throw new IOException( "Writing to an already closed Writer" );
534                 }
535             }
536         });
537 
538         createParser().parse( new StringReader( document1 ), sink );
539         createParser().parse( new StringReader( document2 ), sink );
540     }
541     
542 
543     /**
544      * DOXIA-370
545      *
546      * @throws ParseException
547      */
548     public void testSeparatorInParagraph()
549         throws ParseException
550     {
551         String document = "Up\n---\nDown\n";
552 
553         output = new StringWriter();
554         Sink sink = new TextSink( output );
555 
556         /* parsing with separator in middle of paragraph */
557         createParser().parse( new StringReader( document ), sink );
558         assertTrue( "generated document should have a size > 0", output.toString().length() > 0 );
559 
560     }
561     
562     public void testListFollowedByMacro() throws Exception
563     {
564         // @todo FIX
565         // DOXIA-371
566         String document = "- This is a little test. \r\n" +
567                 "\r\n" + // with extra linebreak it succeeds, without it should too 
568         		"{code}\r\n" + 
569         		"    @Autowired\r\n" + 
570         		"    private DataSource dataSource;\r\n" + 
571         		"{code}\r\n"; 
572         output = new StringWriter();
573         SinkEventTestingSink sink = new SinkEventTestingSink();
574         createParser().parse( new StringReader( document ), sink );
575         
576         Iterator<SinkEventElement> it = sink.getEventList().iterator();
577         assertEquals("head", it.next().getName() );
578         assertEquals("head_", it.next().getName() );
579         assertEquals("body", it.next().getName() );
580         assertEquals("list", it.next().getName() );
581         assertEquals("listItem", it.next().getName() );
582         assertEquals( it.next(), "text", "This is a little test." );
583         assertEquals("listItem_", it.next().getName() );
584         assertEquals("list_", it.next().getName() );
585         assertEquals("verbatim", it.next().getName() );
586         assertEquals( it.next(), "text", "    @Autowired\n    private DataSource dataSource;\n" );
587         assertEquals("verbatim_", it.next().getName() );
588         assertEquals("body_", it.next().getName() );
589     }
590 
591 	public void testLinethrough() throws Exception {
592 		String document = "-Linethrough-";
593 		output = new StringWriter();
594 		SinkEventTestingSink sink = new SinkEventTestingSink();
595 		createParser().parse(new StringReader(document), sink);
596 
597 		Iterator<SinkEventElement> it = sink.getEventList().iterator();
598 		assertEquals(it, "head", "head_", "body", "paragraph");
599 		assertEquals(it.next(), "text", "Linethrough",
600 				new SinkEventAttributeSet("decoration", "line-through"));
601 		assertEquals(it, "paragraph_", "body_");
602 		assertFalse(it.hasNext());
603 	}
604 
605 	public void testUnderline() throws Exception {
606 		String document = "+Underline+";
607 		output = new StringWriter();
608 		SinkEventTestingSink sink = new SinkEventTestingSink();
609 		createParser().parse(new StringReader(document), sink);
610 
611 		Iterator<SinkEventElement> it = sink.getEventList().iterator();
612 		assertEquals(it, "head", "head_", "body", "paragraph");
613 		assertEquals(it.next(), "text", "Underline", new SinkEventAttributeSet(
614 				"decoration", "underline"));
615 		assertEquals(it, "paragraph_", "body_");
616 		assertFalse(it.hasNext());
617 	}
618 
619 	public void testSub() throws Exception {
620 		String document = "~Sub~";
621 		output = new StringWriter();
622 		SinkEventTestingSink sink = new SinkEventTestingSink();
623 		createParser().parse(new StringReader(document), sink);
624 
625 		Iterator<SinkEventElement> it = sink.getEventList().iterator();
626 		assertEquals(it, "head", "head_", "body", "paragraph");
627 		assertEquals(it.next(), "text", "Sub", new SinkEventAttributeSet(
628 				"valign", "sub"));
629 		assertEquals(it, "paragraph_", "body_");
630 		assertFalse(it.hasNext());
631 	}
632 
633 	public void testSup() throws Exception {
634 		String document = "^Sup^";
635 		output = new StringWriter();
636 		SinkEventTestingSink sink = new SinkEventTestingSink();
637 		createParser().parse(new StringReader(document), sink);
638 
639 		Iterator<SinkEventElement> it = sink.getEventList().iterator();
640 		assertEquals(it, "head", "head_", "body", "paragraph");
641 		assertEquals(it.next(), "text", "Sup", new SinkEventAttributeSet(
642 				"valign", "sup"));
643 		assertEquals(it, "paragraph_", "body_");
644 		assertFalse(it.hasNext());
645 	}
646 
647     private void assertContainsLines( String message, String result, String lines )
648     {
649         lines = StringUtils.replace( lines, "\n", EOL );
650         if ( message != null )
651         {
652             assertTrue( message, result.indexOf( lines ) != -1 );
653         }
654         else
655         {
656             assertTrue( result.indexOf( lines ) != -1 );
657         }
658     }
659 
660     private void assertContainsLines( String result, String lines )
661     {
662         this.assertContainsLines( null, result, lines );
663     }
664 
665     private String getLineBreakString()
666     {
667         StringWriter sw = new StringWriter();
668         Sink sink = new TextSink( sw );
669         sink.lineBreak();
670 
671         return sw.toString();
672     }
673 
674     private String locateAndParseTestSourceFile( String stem )
675         throws IOException, ParseException
676     {
677         output = new StringWriter();
678         reader = getTestReader( stem, outputExtension() );
679         writer = getTestWriter( stem, "txt" );
680 
681         Sink sink = new TextSink( output );
682         createParser().parse( reader, sink );
683 
684         // write to file
685         String expected = output.toString();
686         writer.write( expected );
687         writer.flush();
688         return expected;
689     }
690 
691 }