View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   */ 
19  package org.apache.rat.report.xml.writer.impl.base;
20  
21  import org.apache.rat.report.xml.writer.InvalidXmlException;
22  import org.apache.rat.report.xml.writer.OperationNotAllowedException;
23  import org.apache.rat.report.xml.writer.XmlWriter;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  
27  import java.io.StringWriter;
28  
29  import static org.assertj.core.api.Assertions.fail;
30  import static org.junit.jupiter.api.Assertions.assertEquals;
31  import static org.junit.jupiter.api.Assertions.assertFalse;
32  import static org.junit.jupiter.api.Assertions.assertTrue;
33  
34  
35  public class XmlWriterTest {
36  
37      private static final char[] ZERO_CHAR = {(char)0};
38      
39      private XmlWriter writer;
40      private StringWriter out;
41      
42      @BeforeEach
43      public void setUp() {
44          out = new StringWriter();
45          writer = new XmlWriter(out);
46      }
47  
48      @Test
49      public void returnValues() throws Exception {
50          assertEquals( 
51                  writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
52          assertEquals(
53                  writer, writer.attribute("beta", "b"), "XmlWriters should always return themselves");
54          assertEquals(
55                  writer, writer.content("gamma"), "XmlWriters should always return themselves");
56          assertEquals(
57                  writer, writer.closeElement(), "XmlWriters should always return themselves");
58      }
59  
60      @Test
61      public void openElement() throws Exception {
62          assertEquals(
63                  writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
64           assertEquals("<alpha", out.toString(), "Alpha element started");
65          assertEquals(
66                  writer, writer.openElement("beta"), "XmlWriters should always return themselves");
67          assertEquals("<alpha><beta", out.toString(), "Alpha element tag closed and beta started");
68          assertEquals(
69                  writer, writer.closeElement(), "XmlWriters should always return themselves");
70          assertEquals("<alpha><beta/>", out.toString(), "Beta tag ended");
71          assertEquals(
72                  writer, writer.openElement("gamma"), "XmlWriters should always return themselves");
73          assertEquals("<alpha><beta/><gamma", out.toString(), "Gamma tag started");
74      }
75      
76      @Test
77      public void invalidElementName() throws Exception {
78          assertTrue( isValidElementName("alpha"), "All strings ok");
79          assertTrue(isValidElementName("alpha77"), "Strings and digits ok");
80          assertFalse(isValidElementName("5alpha77"), "Must no start with digit");
81          assertFalse(isValidElementName("alph<a77"), "Greater than not ok");
82          assertFalse(isValidElementName("alph<a77"), "Less than not ok");
83          assertFalse(isValidElementName("alph'a77"), "Quote not ok");
84          assertTrue(isValidElementName("alph-a77"), "Dash ok");
85          assertTrue(isValidElementName("alph_a77"), "Underscore ok");
86          assertTrue(isValidElementName("alph.a77"), "Dot ok");
87          assertTrue(isValidElementName("alpha:77"), "Colon ok");
88          assertFalse(isValidElementName("-a77"), "Start with dash not ok");
89          assertTrue(isValidElementName("_a77"), "Start with underscore ok");
90          assertFalse(isValidElementName(".a77"), "Start with dot not ok");
91          assertTrue(isValidElementName(":a77"), "Start with colon ok");
92      }
93      
94      private boolean isValidElementName(String elementName) throws Exception {
95          boolean result = true;
96          try {
97              writer.openElement(elementName);
98          } catch (InvalidXmlException e) {
99              result = false;
100         }
101         return result;
102     }
103 
104     @Test
105     public void callOpenElementAfterLastElementClosed() throws Exception {
106         assertEquals(
107                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
108          assertEquals("<alpha", out.toString(), "Alpha element started");
109         assertEquals(
110                 writer, writer.closeElement(), "XmlWriters should always return themselves");
111         assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
112         try {
113             writer.openElement("delta");
114             fail("Cannot open new elements once the first element has been closed");
115         } catch (OperationNotAllowedException e) {
116             // Cannot open new elements once the first element has been closed
117         }
118     }    
119 
120     @Test
121     public void callCloseElementAfterLastElementClosed() throws Exception {
122         assertEquals(
123                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
124          assertEquals("<alpha", out.toString(), "Alpha element started");
125         assertEquals(
126                 writer, writer.closeElement(), "XmlWriters should always return themselves");
127         assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
128         try {
129             writer.closeElement();
130             fail("Cannot close elements once the first element has been closed");
131         } catch (OperationNotAllowedException e) {
132             // Cannot open new elements once the first element has been closed
133         }
134     }
135 
136     @Test
137     public void closeFirstElement() throws Exception {
138         assertEquals(
139                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
140         assertEquals("<alpha", out.toString(), "Alpha element started");
141         assertEquals(
142                 writer, writer.closeElement(), "XmlWriters should always return themselves");
143         assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
144     }
145     
146     @Test
147     public void closeElementWithContent() throws Exception {
148         assertEquals(
149                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
150         assertEquals("<alpha", out.toString(), "Alpha element started");
151         assertEquals(
152                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
153         assertEquals("<alpha><beta", out.toString(), "Beta element started");
154         assertEquals(
155                 writer, writer.closeElement(), "XmlWriters should always return themselves");
156         assertEquals("<alpha><beta/>", out.toString(), "Element beta is closed");
157         assertEquals(
158                 writer, writer.closeElement(), "XmlWriters should always return themselves");
159         assertEquals("<alpha><beta/></alpha>", out.toString(), "Element beta is closed");
160         try {
161             writer.closeElement();
162             fail("Cannot close elements once the first element has been closed");
163         } catch (OperationNotAllowedException e) {
164             // Cannot open new elements once the first element has been closed
165         }
166     }
167     
168     @Test
169     public void closeElementBeforeFirstElement() throws Exception {
170         try {
171             writer.closeElement();
172             fail("Cannot close elements before the first element has been closed");
173         } catch (OperationNotAllowedException e) {
174             // Cannot open new elements before the first element has been closed
175         }
176     }
177     
178     @Test
179     public void contentAfterElement() throws Exception {
180         assertEquals(
181                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
182          assertEquals("<alpha", out.toString(), "Alpha element started");
183         assertEquals(
184                 writer, writer.content("foo bar"), "XmlWriters should always return themselves");
185         assertEquals("<alpha>foo bar", out.toString(), "Alpha tag closed. Content written");
186         assertEquals(
187                 writer, writer.content(" and more foo bar"), "XmlWriters should always return themselves");
188         assertEquals("<alpha>foo bar and more foo bar", out.toString(), "Alpha tag closed. Content written");
189         assertEquals(
190                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
191         assertEquals("<alpha>foo bar and more foo bar<beta", out.toString());
192         assertEquals(
193                 writer, writer.closeElement(), "XmlWriters should always return themselves");
194         assertEquals("<alpha>foo bar and more foo bar<beta/>", out.toString(), "Element beta is closed");
195         assertEquals(
196                 writer, writer.closeElement(), "XmlWriters should always return themselves");
197         assertEquals("<alpha>foo bar and more foo bar<beta/></alpha>", out.toString(), "Element beta is closed");
198         try {
199             writer.content("A Sentence Too far");
200             fail("Cannot write content once the first element has been closed");
201         } catch (OperationNotAllowedException e) {
202             // Cannot open new elements once the first element has been closed
203         }
204     }
205 
206     @Test
207     public void contentAfterLastElement() throws Exception {
208         assertEquals(
209                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
210          assertEquals("<alpha", out.toString(), "Alpha element started");
211         assertEquals(
212                 writer, writer.closeElement(), "XmlWriters should always return themselves");
213         assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
214         try {
215             writer.content("A Sentence Too far");
216             fail("Cannot write content once the first element has been closed");
217         } catch (OperationNotAllowedException e) {
218             // Cannot open new elements once the first element has been closed
219         }
220     }
221     
222     @Test
223     public void writeContentBeforeFirstElement() throws Exception {
224         try {
225             writer.content("Too early");
226             fail("Cannot close elements before the first element has been closed");
227         } catch (OperationNotAllowedException e) {
228             // Cannot open new elements before the first element has been closed
229         }
230     }
231     
232     @Test
233     public void contentEscaping() throws Exception {
234         assertEquals(
235                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
236          assertEquals("<alpha", out.toString(), "Alpha element started");
237         assertEquals(
238                 writer, writer.content("this&that"), "XmlWriters should always return themselves");
239         assertEquals("<alpha>this&amp;that", out.toString(), "Amphersands must be escaped");
240         assertEquals(
241                 writer, writer.content("small<large"), "XmlWriters should always return themselves");
242         assertEquals("<alpha>this&amp;thatsmall&lt;large", out.toString(), "Left angle brackets must be escaped");
243         assertEquals(
244                 writer, writer.content("12>1"), "XmlWriters should always return themselves");
245         assertEquals("<alpha>this&amp;thatsmall&lt;large12&gt;1", out.toString(), "Choose to escape right angle brackets");
246 
247     }
248 
249     @Test
250     public void attributeAfterLastElement() throws Exception {
251         assertEquals(
252                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
253          assertEquals("<alpha", out.toString(), "Alpha element started");
254         assertEquals(
255                 writer, writer.closeElement(), "XmlWriters should always return themselves");
256         assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
257         try {
258             writer.attribute("foo", "bar");
259             fail("Cannot write content once the first element has been closed");
260         } catch (OperationNotAllowedException e) {
261             // Cannot open new elements once the first element has been closed
262         }
263     }
264     
265     @Test
266     public void attributeContentBeforeFirstElement() throws Exception {
267         try {
268             writer.attribute("foo", "bar");
269             fail("Cannot close elements before the first element has been closed");
270         } catch (OperationNotAllowedException e) {
271             // Cannot open new elements before the first element has been closed
272         }
273     }
274     
275     @Test
276     public void invalidAttributeName() throws Exception {
277         writer.openElement("alpha");
278         assertTrue(isValidAttributeName("alpha"), "All string ok");
279         assertTrue(isValidAttributeName("alpha77"), "Strings and digits ok");
280         assertFalse(isValidAttributeName("5alpha77"), "Must not start with digit");
281         assertTrue(isValidAttributeName("alpha:77"), "Colon ok");
282         assertFalse(isValidAttributeName("alph<a77"),"Greater than not ok");
283         assertFalse(isValidAttributeName("alph<a77"), "Less than not ok");
284         assertFalse(isValidAttributeName("alph'a77"), "Quote not ok");
285     }
286     
287     private boolean isValidAttributeName(String name) throws Exception {
288         boolean result = true;
289         try {
290             writer.attribute(name, "");
291         } catch (InvalidXmlException e) {
292             result = false;
293         }
294         return result;
295     }
296     
297     @Test
298     public void escapeAttributeContent() throws Exception {
299         assertEquals(
300                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
301          assertEquals("<alpha", out.toString(), "Alpha element started");
302         assertEquals(
303                 writer, writer.attribute("one", "this&that"), "XmlWriters should always return themselves");
304         assertEquals("<alpha one='this&amp;that'", out.toString(), "Amphersands must be escaped");
305         assertEquals(
306                 writer, writer.attribute("two", "small<large"), "XmlWriters should always return themselves");
307         assertEquals("<alpha one='this&amp;that' two='small&lt;large'", out.toString(), "Left angle brackets must be escaped");
308         assertEquals(
309                 writer, writer.attribute("three", "12>1"), "XmlWriters should always return themselves");
310         assertEquals("<alpha one='this&amp;that' two='small&lt;large' three='12&gt;1'", out.toString(), "Choose to escape right angle brackets");
311         assertEquals(
312                 writer, writer.attribute("four", "'quote'"), "XmlWriters should always return themselves");
313         assertEquals("<alpha one='this&amp;that' two='small&lt;large' three='12&gt;1' four='&apos;quote&apos;'", out.toString(), "Apostrophes must be escape");
314         assertEquals(
315                 writer, writer.attribute("five", "\"quote\""), "XmlWriters should always return themselves");
316         assertEquals("<alpha one='this&amp;that' two='small&lt;large' three='12&gt;1' four='&apos;quote&apos;' five='&quot;quote&quot;'", out.toString(), "Double quotes must be escape");
317 
318     }
319     
320     @Test
321     public void attributeInContent() throws Exception {
322         assertEquals(
323                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
324          assertEquals("<alpha", out.toString(), "Alpha element started");
325         assertEquals(
326                 writer, writer.content("foo bar"), "XmlWriters should always return themselves");
327         try {
328             writer.attribute("name", "value");
329             fail("attributes after body content are not allowed");
330         } catch (InvalidXmlException e) {
331             // attributes after body content are not allowed
332         }
333     }
334   
335     @Test
336     public void outOfRangeCharacter() throws Exception {
337         assertEquals(
338                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
339          assertEquals("<alpha", out.toString(), "Alpha element started");
340         assertEquals(
341                 writer, writer.content(new String(ZERO_CHAR)), "XmlWriters should always return themselves");
342         String out = this.out.toString();
343         assertEquals("<alpha>?", out, "Replace illegal characters with question marks");
344     }
345     
346     @Test
347     public void attributeAfterElementClosed() throws Exception {
348         assertEquals(
349                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
350          assertEquals("<alpha", out.toString(), "Alpha element started");
351         assertEquals(
352                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
353         assertEquals("<alpha><beta", out.toString(), "Beta element started");
354         assertEquals(
355                 writer, writer.closeElement(), "XmlWriters should always return themselves");
356         assertEquals("<alpha><beta/>", out.toString(), "Beta element closed");
357         try {
358             writer.attribute("name", "value");
359             fail("attributes after closed element are not allowed");
360         } catch (InvalidXmlException e) {
361             // attributes after body content are not allowed
362         }
363     }
364     
365     @Test
366     public void closeDocumentBeforeOpen() throws Exception {
367         try {
368             writer.closeDocument();
369             fail("Cannot close document before the first element has been opened");
370         } catch (OperationNotAllowedException e) {
371             // Cannot open new elements before the first element has been opened
372         }
373     }
374     
375     @Test
376     public void closeDocumentAfterRootElementClosed() throws Exception {
377         assertEquals(
378                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
379          assertEquals("<alpha", out.toString(), "Alpha element started");
380         assertEquals(
381                 writer, writer.closeElement(), "XmlWriters should always return themselves");
382         assertEquals("<alpha/>", out.toString());
383         try {
384             writer.closeDocument();
385         } catch (OperationNotAllowedException e) {
386             fail("No exception should be thrown when called after the root element is closed.");
387         }
388     }   
389     
390     @Test
391     public void closeSimpleDocument() throws Exception {
392         assertEquals(
393                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
394          assertEquals("<alpha", out.toString(), "Alpha element started");
395         assertEquals(
396                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
397         assertEquals("<alpha><beta", out.toString(), "Beta element started");
398         assertEquals(
399                 writer, writer.closeDocument(), "XmlWriters should always return themselves");
400         assertEquals("<alpha><beta/></alpha>", out.toString(), "Beta element started");
401     }
402     
403     @Test
404     public void closeComplexDocument() throws Exception {
405         assertEquals(
406                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
407          assertEquals("<alpha", out.toString(), "Alpha element started");
408         assertEquals(
409                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
410         assertEquals("<alpha><beta", out.toString(), "Beta element started");
411         assertEquals(
412                 writer, writer.attribute("name", "value"), "XmlWriters should always return themselves");
413         assertEquals("<alpha><beta name='value'", out.toString(), "Beta element started");
414         assertEquals(
415                 writer, writer.closeElement(), "XmlWriters should always return themselves");
416         assertEquals("<alpha><beta name='value'/>", out.toString(), "Beta element started");
417         assertEquals(
418                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
419         assertEquals("<alpha><beta name='value'/><beta", out.toString(), "Beta element started");
420         assertEquals(
421                 writer, writer.attribute("name", "value"), "XmlWriters should always return themselves");
422         assertEquals("<alpha><beta name='value'/><beta name='value'", out.toString(), "Beta element started");
423         assertEquals(
424                 writer, writer.openElement("gamma"), "XmlWriters should always return themselves");
425         assertEquals("<alpha><beta name='value'/><beta name='value'><gamma", out.toString(), "Beta element started");
426         assertEquals(
427                 writer, writer.closeDocument(), "XmlWriters should always return themselves");
428         assertEquals("<alpha><beta name='value'/><beta name='value'><gamma/></beta></alpha>", out.toString(), "Beta element started");
429     }
430     
431     @Test
432     public void writeProlog() throws Exception {
433         assertEquals(
434                 writer, writer.startDocument(), "XmlWriters should always return themselves");
435         assertEquals("<?xml version='1.0'?>", out.toString(), "Prolog written");
436     }
437     
438     @Test
439     public void writeAfterElement() throws Exception {
440         assertEquals(
441                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
442          assertEquals("<alpha", out.toString(), "Alpha element started");
443         try {
444             writer.startDocument();
445             fail("Operation not allowed once an element has been written");
446         } catch (OperationNotAllowedException e) {
447             // Operation not allowed once an element has been written
448         }
449     }
450     
451     @Test
452     public void writePrologTwo() throws Exception {
453         assertEquals(
454                 writer, writer.startDocument(), "XmlWriters should always return themselves");
455         assertEquals("<?xml version='1.0'?>", out.toString(), "Prolog written");
456         try {
457             writer.startDocument();
458             fail("Operation not allow once a prolog has been written");
459         } catch (OperationNotAllowedException e) {
460             // Operation not allowed once an prolog has been written
461         }
462     }
463     
464     @Test
465     public void duplicateAttributes() throws Exception {
466         assertEquals(
467                 writer, writer.openElement("alpha"), "XmlWriters should always return themselves");
468          assertEquals("<alpha", out.toString(), "Alpha element started");
469         assertEquals(
470                 writer, writer.attribute("one", "1"), "XmlWriters should always return themselves");
471         assertEquals("<alpha one='1'", out.toString(), "Attribute written");
472         assertEquals(
473                 writer, writer.openElement("beta"), "XmlWriters should always return themselves");
474         assertEquals("<alpha one='1'><beta", out.toString(), "Beta element started");
475         assertEquals(
476                 writer, writer.attribute("one", "1"), "XmlWriters should always return themselves");
477         assertEquals("<alpha one='1'><beta one='1'", out.toString(), "Beta element started");
478         try {
479             writer.attribute("one", "2");
480             fail("Each attribute may only be written once");
481         } catch (InvalidXmlException e) {
482             // Each attribute may only be written once
483         }
484     }
485 }