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