View Javadoc

1   /* $Id: DigesterTestCase.java 1212599 2011-12-09 19:46:42Z simonetripodi $
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one or more
4    * contributor license agreements.  See the NOTICE file distributed with
5    * this work for additional information regarding copyright ownership.
6    * The ASF licenses this file to You under the Apache License, Version 2.0
7    * (the "License"); you may not use this file except in compliance with
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.commons.digester3;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertNotSame;
25  import static org.junit.Assert.assertNull;
26  import static org.junit.Assert.assertSame;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  
30  import java.io.File;
31  import java.io.InputStream;
32  import java.io.Reader;
33  import java.io.StringReader;
34  import java.math.BigDecimal;
35  import java.net.URL;
36  import java.util.ArrayList;
37  import java.util.EmptyStackException;
38  import java.util.Map;
39  
40  import org.apache.commons.digester3.Digester;
41  import org.apache.commons.digester3.ObjectCreateRule;
42  import org.apache.commons.digester3.Rule;
43  import org.apache.commons.digester3.RulesBase;
44  import org.apache.commons.digester3.StackAction;
45  import org.apache.commons.digester3.Substitutor;
46  import org.junit.After;
47  import org.junit.Before;
48  import org.junit.Test;
49  import org.xml.sax.Attributes;
50  import org.xml.sax.InputSource;
51  import org.xml.sax.helpers.AttributesImpl;
52  
53  /**
54   * <p>
55   * Test Case for the Digester class. These tests exercise the individual methods of a Digester, but do not attempt to
56   * process complete documents.
57   * </p>
58   *
59   * @author Craig R. McClanahan
60   */
61  public class DigesterTestCase
62  {
63  
64      // ----------------------------------------------------- Instance Variables
65  
66      /**
67       * The digester instance we will be processing.
68       */
69      protected Digester digester = null;
70  
71      /**
72       * The set of public identifiers, and corresponding resource names, for the versions of the DTDs that we know about.
73       * There <strong>MUST</strong> be an even number of Strings in this array.
74       */
75      protected static final String registrations[] = { "-//Netscape Communications//DTD RSS 0.9//EN",
76          "/org/apache/commons/digester3/rss/rss-0.9.dtd", "-//Netscape Communications//DTD RSS 0.91//EN",
77          "/org/apache/commons/digester3/rss/rss-0.91.dtd", };
78  
79      // -------------------------------------------------- Overall Test Methods
80  
81      /**
82       * Set up instance variables required by this test case.
83       */
84      @Before
85      public void setUp()
86      {
87  
88          digester = new Digester();
89          digester.setRules( new RulesBase() );
90  
91      }
92  
93      /**
94       * Tear down instance variables required by this test case.
95       */
96      @After
97      public void tearDown()
98      {
99  
100         digester = null;
101 
102     }
103 
104     // ------------------------------------------------ Individual Test Methods
105 
106     /**
107      * Test <code>null</code> parsing. (should lead to <code>IllegalArgumentException</code>s)
108      */
109     @Test
110     public void testNullFileParse()
111         throws Exception
112     {
113 
114         try
115         {
116             digester.parse( (File) null );
117             fail( "Expected IllegalArgumentException with null argument" );
118         }
119         catch ( IllegalArgumentException e )
120         {
121             // expected
122         }
123 
124     }
125 
126     @Test
127     public void testNullInputSourceParse()
128         throws Exception
129     {
130 
131         try
132         {
133             digester.parse( (InputSource) null );
134             fail( "Expected IllegalArgumentException with null argument" );
135         }
136         catch ( IllegalArgumentException e )
137         {
138             // expected
139         }
140 
141     }
142 
143     @Test
144     public void testNullInputStreamParse()
145         throws Exception
146     {
147 
148         try
149         {
150             digester.parse( (InputStream) null );
151             fail( "Expected IllegalArgumentException with null argument" );
152         }
153         catch ( IllegalArgumentException e )
154         {
155             // expected
156         }
157 
158     }
159 
160     @Test
161     public void testNullReaderParse()
162         throws Exception
163     {
164 
165         try
166         {
167             digester.parse( (Reader) null );
168             fail( "Expected IllegalArgumentException with null argument" );
169         }
170         catch ( IllegalArgumentException e )
171         {
172             // expected
173         }
174 
175     }
176 
177     @Test
178     public void testNullStringParse()
179         throws Exception
180     {
181 
182         try
183         {
184             digester.parse( (String) null );
185             fail( "Expected IllegalArgumentException with null argument" );
186         }
187         catch ( IllegalArgumentException e )
188         {
189             // expected
190         }
191 
192     }
193 
194     @Test
195     public void testNullURLParse()
196         throws Exception
197     {
198 
199         try
200         {
201             digester.parse( (URL) null );
202             fail( "Expected IllegalArgumentException with null argument" );
203         }
204         catch ( IllegalArgumentException e )
205         {
206             // expected
207         }
208 
209     }
210 
211     /**
212      * Test the basic property getters and setters.
213      */
214     @Test
215     public void testProperties()
216     {
217 
218         assertNull( "Initial error handler is null", digester.getErrorHandler() );
219         digester.setErrorHandler( digester );
220         assertTrue( "Set error handler is digester", digester.getErrorHandler() == digester );
221         digester.setErrorHandler( null );
222         assertNull( "Reset error handler is null", digester.getErrorHandler() );
223 
224         assertTrue( "Initial namespace aware is false", !digester.getNamespaceAware() );
225         digester.setNamespaceAware( true );
226         assertTrue( "Set namespace aware is true", digester.getNamespaceAware() );
227         digester.setNamespaceAware( false );
228         assertTrue( "Reset namespace aware is false", !digester.getNamespaceAware() );
229 
230         assertTrue( "Initial validating is false", !digester.getValidating() );
231         digester.setValidating( true );
232         assertTrue( "Set validating is true", digester.getValidating() );
233         digester.setValidating( false );
234         assertTrue( "Reset validating is false", !digester.getValidating() );
235 
236     }
237 
238     /**
239      * Test registration of URLs for specified public identifiers.
240      */
241     @Test
242     public void testRegistrations()
243     {
244 
245         Map<String, URL> map = digester.getRegistrations();
246         assertEquals( "Initially zero registrations", 0, map.size() );
247         int n = 0;
248         for ( int i = 0; i < registrations.length; i += 2 )
249         {
250             URL url = this.getClass().getResource( registrations[i + 1] );
251             if ( url != null )
252             {
253                 digester.register( registrations[i], url );
254                 n++;
255             }
256         }
257         map = digester.getRegistrations();
258         assertEquals( "Registered two URLs", n, map.size() );
259 
260         int count[] = new int[n];
261         for ( int i = 0; i < n; i++ )
262             count[i] = 0;
263         for ( String key : map.keySet() )
264         {
265             for ( int i = 0; i < n; i++ )
266             {
267                 if ( key.equals( registrations[i * 2] ) )
268                 {
269                     count[i]++;
270                     break;
271                 }
272             }
273         }
274         for ( int i = 0; i < n; i++ )
275             assertEquals( "Count for key " + registrations[i * 2], 1, count[i] );
276 
277     }
278 
279     /**
280      * Basic test for rule creation and matching.
281      */
282     @Test
283     public void testRules()
284     {
285 
286         assertEquals( "Initial rules list is empty", 0, digester.getRules().match( null, "a", null, null ).size() );
287         digester.addSetProperties( "a" );
288         assertEquals( "Add a matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
289         digester.addSetProperties( "b" );
290         assertEquals( "Add a non-matching rule", 1, digester.getRules().match( null, "a", null, null ).size() );
291         digester.addSetProperties( "a/b" );
292         assertEquals( "Add a non-matching nested rule", 1, digester.getRules().match( null, "a", null, null ).size() );
293         digester.addSetProperties( "a/b" );
294         assertEquals( "Add a second matching rule", 2, digester.getRules().match( null, "a/b", null, null ).size() );
295 
296     }
297 
298     /**
299      * <p>
300      * Test matching rules in {@link RulesBase}.
301      * </p>
302      * <p>
303      * Tests:
304      * </p>
305      * <ul>
306      * <li>exact match</li>
307      * <li>tail match</li>
308      * <li>longest pattern rule</li>
309      * </ul>
310      */
311     @Test
312     public void testRulesBase()
313     {
314 
315         assertEquals( "Initial rules list is empty", 0, digester.getRules().rules().size() );
316 
317         // We're going to set up
318         digester.addRule( "a/b/c/d", new TestRule( "a/b/c/d" ) );
319         digester.addRule( "*/d", new TestRule( "*/d" ) );
320         digester.addRule( "*/c/d", new TestRule( "*/c/d" ) );
321 
322         // Test exact match
323         assertEquals( "Exact match takes precedence 1", 1, digester.getRules().match( null, "a/b/c/d", null, null ).size() );
324         assertEquals( "Exact match takes precedence 2", "a/b/c/d",
325                       ( (TestRule) digester.getRules().match( null, "a/b/c/d", null, null ).iterator().next() ).getIdentifier() );
326 
327         // Test wildcard tail matching
328         assertEquals( "Wildcard tail matching rule 1", 1, digester.getRules().match( null, "a/b/d", null, null ).size() );
329         assertEquals( "Wildcard tail matching rule 2", "*/d",
330                       ( (TestRule) digester.getRules().match( null, "a/b/d", null, null ).iterator().next() ).getIdentifier() );
331 
332         // Test the longest matching pattern rule
333         assertEquals( "Longest tail rule 1", 1, digester.getRules().match( null, "x/c/d", null, null ).size() );
334         assertEquals( "Longest tail rule 2", "*/c/d",
335                       ( (TestRule) digester.getRules().match( null, "x/c/d", null, null ).iterator().next() ).getIdentifier() );
336 
337     }
338 
339     /**
340      * Test the basic stack mechanisms.
341      */
342     @Test
343     public void testStackMethods()
344     {
345 
346         Object value = null;
347 
348         // New stack must be empty
349         assertEquals( "New stack is empty", 0, digester.getCount() );
350         value = digester.peek();
351         assertNull( "New stack peek() returns null", value );
352         value = digester.pop();
353         assertNull( "New stack pop() returns null", value );
354 
355         // Test pushing and popping activities
356         digester.push( "First Item" );
357         assertEquals( "Pushed one item size", 1, digester.getCount() );
358         value = digester.peek();
359         assertNotNull( "Peeked first item is not null", value );
360         assertEquals( "Peeked first item value", "First Item", value );
361 
362         digester.push( "Second Item" );
363         assertEquals( "Pushed two items size", 2, digester.getCount() );
364         value = digester.peek();
365         assertNotNull( "Peeked second item is not null", value );
366         assertEquals( "Peeked second item value", "Second Item", value );
367 
368         value = digester.pop();
369         assertEquals( "Popped stack size", 1, digester.getCount() );
370         assertNotNull( "Popped second item is not null", value );
371         assertEquals( "Popped second item value", "Second Item", value );
372         value = digester.peek();
373         assertNotNull( "Remaining item is not null", value );
374         assertEquals( "Remaining item value", "First Item", value );
375         assertEquals( "Remaining stack size", 1, digester.getCount() );
376 
377         // Cleared stack is empty
378         digester.push( "Dummy Item" );
379         digester.clear();
380         assertEquals( "Cleared stack is empty", 0, digester.getCount() );
381         value = digester.peek();
382         assertNull( "Cleared stack peek() returns null", value );
383         value = digester.pop();
384         assertNull( "Cleared stack pop() returns null", value );
385 
386     }
387 
388     @Test
389     public void testOnceAndOnceOnly()
390         throws Exception
391     {
392 
393         class TestConfigureDigester
394             extends Digester
395         {
396             public int called = 0;
397 
398             public TestConfigureDigester()
399             {
400             }
401 
402             @Override
403             protected void initialize()
404             {
405                 called++;
406             }
407         }
408 
409         TestConfigureDigester digester = new TestConfigureDigester();
410 
411         String xml = "<?xml version='1.0'?><document/>";
412         digester.parse( new StringReader( xml ) );
413 
414         assertEquals( "Initialize should be called once and only once", 1, digester.called );
415     }
416 
417     @Test
418     public void testBasicSubstitution()
419         throws Exception
420     {
421         class TestSubRule
422             extends Rule
423         {
424             public String body;
425 
426             public Attributes attributes;
427 
428             @Override
429             public void begin( String namespace, String name, Attributes attributes )
430             {
431                 this.attributes = new AttributesImpl( attributes );
432             }
433 
434             @Override
435             public void body( String namespace, String name, String text )
436             {
437                 this.body = text;
438             }
439         }
440 
441         TestSubRule tsr = new TestSubRule();
442         Digester digester = new Digester();
443         digester.addRule( "alpha/beta", tsr );
444 
445         // it's not easy to transform dirty harry into the mighty circus - but let's give it a try
446         String xml =
447             "<?xml version='1.0'?><alpha><beta forname='Dirty' surname='Harry'>Do you feel luck punk?</beta></alpha>";
448         InputSource in = new InputSource( new StringReader( xml ) );
449 
450         digester.parse( in );
451 
452         assertEquals( "Unsubstituted body text", "Do you feel luck punk?", tsr.body );
453         assertEquals( "Unsubstituted number of attributes", 2, tsr.attributes.getLength() );
454         assertEquals( "Unsubstituted forname attribute value", "Dirty", tsr.attributes.getValue( "forname" ) );
455         assertEquals( "Unsubstituted surname attribute value", "Harry", tsr.attributes.getValue( "surname" ) );
456 
457         digester.setSubstitutor( new Substitutor()
458         {
459             @Override
460             public Attributes substitute( Attributes attributes )
461             {
462                 AttributesImpl results = new AttributesImpl();
463                 results.addAttribute( "", "python", "python", "CDATA", "Cleese" );
464                 return results;
465             }
466 
467             @Override
468             public String substitute( String bodyText )
469             {
470                 return "And now for something completely different...";
471             }
472         } );
473 
474         // now transform into the full monty
475         in = new InputSource( new StringReader( xml ) );
476         digester.parse( in );
477 
478         assertEquals( "Substituted body text", "And now for something completely different...", tsr.body );
479         assertEquals( "Substituted number of attributes", 1, tsr.attributes.getLength() );
480         assertEquals( "Substituted python attribute value", "Cleese", tsr.attributes.getValue( "", "python" ) );
481     }
482 
483     /** Tests the push-peek-pop cycle for a named stack */
484     @Test
485     public void testNamedStackPushPeekPop()
486         throws Exception
487     {
488         BigDecimal archimedesAveragePi = new BigDecimal( "3.1418" );
489         String testStackName = "org.apache.commons.digester3.tests.testNamedStackPushPeekPop";
490         Digester digester = new Digester();
491         assertTrue( "Stack starts empty:", digester.isEmpty( testStackName ) );
492         digester.push( testStackName, archimedesAveragePi );
493         assertEquals( "Peeked value:", archimedesAveragePi, digester.peek( testStackName ) );
494         assertEquals( "Popped value:", archimedesAveragePi, digester.pop( testStackName ) );
495         assertTrue( "Stack ends empty:", digester.isEmpty( testStackName ) );
496 
497         digester.push( testStackName, "1" );
498         digester.push( testStackName, "2" );
499         digester.push( testStackName, "3" );
500 
501         assertEquals( "Peek#1", "1", digester.peek( testStackName, 2 ) );
502         assertEquals( "Peek#2", "2", digester.peek( testStackName, 1 ) );
503         assertEquals( "Peek#3", "3", digester.peek( testStackName, 0 ) );
504         assertEquals( "Peek#3a", "3", digester.peek( testStackName ) );
505 
506         try
507         {
508             // peek beyond stack
509             digester.peek( testStackName, 3 );
510             fail( "Peek#4 failed to throw an exception." );
511         }
512         catch ( EmptyStackException ex )
513         {
514             // ok, expected
515         }
516 
517         try
518         {
519             // peek a nonexistent named stack
520             digester.peek( "no.such.stack", 0 );
521             fail( "Peeking a non-existent stack failed to throw an exception." );
522         }
523         catch ( EmptyStackException ex )
524         {
525             // ok, expected
526         }
527     }
528 
529     /** Tests that values are stored independently */
530     @Test
531     public void testNamedIndependence()
532     {
533         String testStackOneName = "org.apache.commons.digester3.tests.testNamedIndependenceOne";
534         String testStackTwoName = "org.apache.commons.digester3.tests.testNamedIndependenceTwo";
535         Digester digester = new Digester();
536         digester.push( testStackOneName, "Tweedledum" );
537         digester.push( testStackTwoName, "Tweedledee" );
538         assertEquals( "Popped value one:", "Tweedledum", digester.pop( testStackOneName ) );
539         assertEquals( "Popped value two:", "Tweedledee", digester.pop( testStackTwoName ) );
540     }
541 
542     /** Tests popping named stack not yet pushed */
543     @Test
544     public void testPopNamedStackNotPushed()
545     {
546         String testStackName = "org.apache.commons.digester3.tests.testPopNamedStackNotPushed";
547         Digester digester = new Digester();
548         try
549         {
550 
551             digester.pop( testStackName );
552             fail( "Expected an EmptyStackException" );
553 
554         }
555         catch ( EmptyStackException e )
556         {
557             // expected
558         }
559 
560         try
561         {
562 
563             digester.peek( testStackName );
564             fail( "Expected an EmptyStackException" );
565 
566         }
567         catch ( EmptyStackException e )
568         {
569             // expected
570         }
571     }
572 
573     /** Tests for isEmpty */
574     @Test
575     public void testNamedStackIsEmpty()
576     {
577         String testStackName = "org.apache.commons.digester3.tests.testNamedStackIsEmpty";
578         Digester digester = new Digester();
579         assertTrue( "A named stack that has no object pushed onto it yet should be empty",
580                     digester.isEmpty( testStackName ) );
581 
582         digester.push( testStackName, "Some test value" );
583         assertFalse( "A named stack that has an object pushed onto it should be not empty",
584                      digester.isEmpty( testStackName ) );
585 
586         digester.peek( testStackName );
587         assertFalse( "Peek should not effect whether the stack is empty", digester.isEmpty( testStackName ) );
588 
589         digester.pop( testStackName );
590         assertTrue( "A named stack that has it's last object popped is empty", digester.isEmpty( testStackName ) );
591     }
592 
593     /**
594      * Test the Digester.getRoot method.
595      */
596     @Test
597     public void testGetRoot()
598         throws Exception
599     {
600         Digester digester = new Digester();
601         digester.addRule( "root", new ObjectCreateRule( TestBean.class ) );
602 
603         String xml = "<root/>";
604         InputSource in = new InputSource( new StringReader( xml ) );
605 
606         digester.parse( in );
607 
608         Object root = digester.getRoot();
609         assertNotNull( "root object not retrieved", root );
610         assertTrue( "root object not a TestRule instance", ( root instanceof TestBean ) );
611     }
612 
613     /** Utility class for method testStackAction */
614     private static class TrackingStackAction
615         implements StackAction
616     {
617         public ArrayList<String> events = new ArrayList<String>();
618 
619         public Object onPush( Digester d, String stackName, Object o )
620         {
621             String msg = "push:" + stackName + ":" + o.toString();
622             events.add( msg );
623 
624             String str = o.toString();
625             if ( str.startsWith( "replpush" ) )
626             {
627                 return new String( str );
628             }
629             return o;
630         }
631 
632         public Object onPop( Digester d, String stackName, Object o )
633         {
634             String msg = "pop:" + stackName + ":" + o.toString();
635             events.add( msg );
636             String str = o.toString();
637             if ( str.startsWith( "replpop" ) )
638             {
639                 return new String( str );
640             }
641             return o;
642         }
643     }
644 
645     /**
646      * Test custom StackAction subclasses.
647      */
648     @Test
649     public void testStackAction()
650     {
651         TrackingStackAction action = new TrackingStackAction();
652 
653         Object obj1 = new String( "obj1" );
654         Object obj2 = new String( "obj2" );
655         Object obj3 = new String( "replpop.obj3" );
656         Object obj4 = new String( "replpush.obj4" );
657 
658         Object obj8 = new String( "obj8" );
659         Object obj9 = new String( "obj9" );
660 
661         Digester d = new Digester();
662         d.setStackAction( action );
663 
664         assertEquals( 0, action.events.size() );
665         d.push( obj1 );
666         d.push( obj2 );
667         d.push( obj3 );
668         d.push( obj4 );
669 
670         assertNotNull( d.peek( 0 ) );
671         // for obj4, a copy should have been pushed
672         assertNotSame( obj4, d.peek( 0 ) );
673         assertEquals( obj4, d.peek( 0 ) );
674         // for obj3, replacement only occurs on pop
675         assertSame( obj3, d.peek( 1 ) );
676         assertSame( obj2, d.peek( 2 ) );
677         assertSame( obj1, d.peek( 3 ) );
678 
679         Object obj4a = d.pop();
680         Object obj3a = d.pop();
681         Object obj2a = d.pop();
682         Object obj1a = d.pop();
683 
684         assertFalse( obj4 == obj4a );
685         assertEquals( obj4, obj4a );
686         assertFalse( obj3 == obj4a );
687         assertEquals( obj3, obj3a );
688         assertSame( obj2, obj2a );
689         assertSame( obj1, obj1a );
690 
691         d.push( "stack1", obj8 );
692         d.push( "stack1", obj9 );
693         Object obj9a = d.pop( "stack1" );
694         Object obj8a = d.pop( "stack1" );
695 
696         assertSame( obj8, obj8a );
697         assertSame( obj9, obj9a );
698 
699         assertEquals( 12, action.events.size() );
700         assertEquals( "push:null:obj1", action.events.get( 0 ) );
701         assertEquals( "push:null:obj2", action.events.get( 1 ) );
702         assertEquals( "push:null:replpop.obj3", action.events.get( 2 ) );
703         assertEquals( "push:null:replpush.obj4", action.events.get( 3 ) );
704         assertEquals( "pop:null:replpush.obj4", action.events.get( 4 ) );
705         assertEquals( "pop:null:replpop.obj3", action.events.get( 5 ) );
706         assertEquals( "pop:null:obj2", action.events.get( 6 ) );
707         assertEquals( "pop:null:obj1", action.events.get( 7 ) );
708 
709         assertEquals( "push:stack1:obj8", action.events.get( 8 ) );
710         assertEquals( "push:stack1:obj9", action.events.get( 9 ) );
711         assertEquals( "pop:stack1:obj9", action.events.get( 10 ) );
712         assertEquals( "pop:stack1:obj8", action.events.get( 11 ) );
713     }
714 }