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 javax.faces.component;
20  
21  import static org.easymock.EasyMock.anyObject;
22  import static org.easymock.EasyMock.aryEq;
23  import static org.easymock.EasyMock.eq;
24  import static org.easymock.EasyMock.expect;
25  import static org.easymock.EasyMock.getCurrentArguments;
26  import static org.junit.Assert.assertEquals;
27  import static org.junit.Assert.assertNull;
28  import static org.junit.Assert.fail;
29  
30  import java.lang.reflect.Method;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Iterator;
35  import java.util.Locale;
36  import java.util.Map;
37  
38  import javax.el.ELContext;
39  import javax.el.MethodExpression;
40  import javax.faces.FactoryFinder;
41  import javax.faces.application.Application;
42  import javax.faces.application.ProjectStage;
43  import javax.faces.application.ViewHandler;
44  import javax.faces.context.ExternalContext;
45  import javax.faces.event.AbortProcessingException;
46  import javax.faces.event.ActionEvent;
47  import javax.faces.event.ActionListener;
48  import javax.faces.event.PhaseEvent;
49  import javax.faces.event.PhaseId;
50  import javax.faces.event.PhaseListener;
51  import javax.faces.lifecycle.Lifecycle;
52  import javax.faces.lifecycle.LifecycleFactory;
53  import javax.faces.webapp.FacesServlet;
54  
55  import org.apache.myfaces.TestRunner;
56  import org.apache.myfaces.test.base.junit4.AbstractJsfTestCase;
57  import org.apache.myfaces.test.mock.MockFacesContext12;
58  import org.easymock.IAnswer;
59  import org.easymock.classextension.EasyMock;
60  import org.easymock.classextension.IMocksControl;
61  import org.junit.After;
62  import org.junit.Before;
63  import org.junit.Test;
64  
65  
66  
67  
68  /**
69   * @author Mathias Broekelmann (latest modification by $Author: struberg $)
70   * @version $Revision: 1188235 $ $Date: 2011-10-24 12:09:33 -0500 (Mon, 24 Oct 2011) $
71   */
72  public class UIViewRootTest extends AbstractJsfTestCase
73  {
74      private Map<PhaseId, Class<? extends PhaseListener>> phaseListenerClasses;
75      private IMocksControl _mocksControl;
76      private MockFacesContext12 _facesContext;
77      private UIViewRoot _testimpl;
78      private ExternalContext _externalContext;
79      private Application _application;
80      private Lifecycle _lifecycle;
81      private LifecycleFactory _lifecycleFactory;
82      private ViewHandler _viewHandler;
83      private ELContext _elContext;
84  
85      private static ThreadLocal<LifecycleFactory> LIFECYCLEFACTORY = new ThreadLocal<LifecycleFactory>();
86  
87      @Before
88      public void setUp() throws Exception
89      {
90          super.setUp();
91          phaseListenerClasses = new HashMap<PhaseId, Class<? extends PhaseListener>>();
92          phaseListenerClasses.put(PhaseId.APPLY_REQUEST_VALUES, ApplyRequesValuesPhaseListener.class);
93          phaseListenerClasses.put(PhaseId.PROCESS_VALIDATIONS, ProcessValidationsPhaseListener.class);
94          phaseListenerClasses.put(PhaseId.UPDATE_MODEL_VALUES, UpdateModelValuesPhaseListener.class);
95          phaseListenerClasses.put(PhaseId.INVOKE_APPLICATION, InvokeApplicationPhaseListener.class);
96          phaseListenerClasses.put(PhaseId.RENDER_RESPONSE, RenderResponsePhaseListener.class);
97  
98          _mocksControl = EasyMock.createControl();
99          _externalContext = _mocksControl.createMock(ExternalContext.class);
100         _facesContext = (MockFacesContext12) facesContext;
101         _application = _mocksControl.createMock(Application.class);
102         _lifecycleFactory = _mocksControl.createMock(LifecycleFactory.class);
103         _testimpl = new UIViewRoot();
104         _lifecycle = _mocksControl.createMock(Lifecycle.class);
105         _elContext = _mocksControl.createMock(ELContext.class);
106         _viewHandler = _mocksControl.createMock(ViewHandler.class);
107         _facesContext.setELContext(_elContext);
108 
109         LIFECYCLEFACTORY.set(_lifecycleFactory);
110         FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY, MockLifeCycleFactory.class.getName());
111         servletContext.addInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME, ProjectStage.UnitTest.name());
112         
113     }
114 
115     @After
116     public void tearDown() throws Exception
117     {
118         super.tearDown();
119         _mocksControl.reset();
120     }
121 
122     @Test
123     public void testSuperClass() throws Exception
124     {
125         assertEquals(UIComponentBase.class, UIViewRoot.class.getSuperclass());
126     }
127 
128     @Test
129     public void testComponentType() throws Exception
130     {
131         assertEquals("javax.faces.ViewRoot", UIViewRoot.COMPONENT_TYPE);
132     }
133 
134     @Test
135     public void testLocale() throws Exception
136     {
137         expect(_application.getViewHandler()).andReturn(_viewHandler).anyTimes();
138         expect(_viewHandler.calculateLocale(_facesContext)).andReturn(null).anyTimes();
139         _mocksControl.replay();
140 
141         _facesContext.setApplication(_application);
142         assertNull(_testimpl.getLocale());
143         _testimpl.setLocale(Locale.JAPANESE);
144         assertEquals(Locale.JAPANESE, _testimpl.getLocale());
145         _mocksControl.verify();
146     }
147 
148     /**
149      * Test method for {@link javax.faces.component.UIViewRoot#createUniqueId()}.
150      */
151     @Test
152     public void testCreateUniqueId()
153     {
154         /*
155         expect(_externalContext.encodeNamespace((String) anyObject())).andAnswer(new IAnswer<String>()
156         {
157             public String answer() throws Throwable
158             {
159                 return (String) getCurrentArguments()[0];
160             }
161         }).anyTimes();*/
162         _mocksControl.replay();
163         Collection<String> createdIds = new HashSet<String>();
164         for (int i = 0; i < 10000; i++)
165         {
166             if (!createdIds.add(_testimpl.createUniqueId()))
167             {
168                 fail("duplicate id created");
169             }
170         }
171         _mocksControl.verify();
172     }
173 
174 // Disabled until Myfaces test issues are resolved..
175 //    /**
176 //     * Test method for {@link javax.faces.component.UIViewRoot#processDecodes(javax.faces.context.FacesContext)}.
177 //     * 
178 //     * @throws Throwable
179 //     */
180 //    @Test
181 //    public void testProcessDecodes() throws Throwable
182 //    {
183 //        testProcessXXX(new TestRunner()
184 //        {
185 //            public void run() throws Throwable
186 //            {
187 //                _testimpl.processDecodes(_facesContext);
188 //            }
189 //        }, PhaseId.APPLY_REQUEST_VALUES, false, true, true);
190 //    }
191 //
192 //    /**
193 //     * Test method for {@link javax.faces.component.UIViewRoot#processValidators(javax.faces.context.FacesContext)}.
194 //     * 
195 //     * @throws Throwable
196 //     */
197 //    @Test
198 //    public void testProcessValidators() throws Throwable
199 //    {
200 //        testProcessXXX(new TestRunner()
201 //        {
202 //            public void run() throws Throwable
203 //            {
204 //                _testimpl.processValidators(_facesContext);
205 //            }
206 //        }, PhaseId.PROCESS_VALIDATIONS, false, true, true);
207 //    }
208 //
209 //    /**
210 //     * Test method for {@link javax.faces.component.UIViewRoot#processUpdates(javax.faces.context.FacesContext)}.
211 //     * 
212 //     * @throws Throwable
213 //     */
214 //    @Test
215 //    public void testProcessUpdates() throws Throwable
216 //    {
217 //        testProcessXXX(new TestRunner()
218 //        {
219 //            public void run() throws Throwable
220 //            {
221 //                _testimpl.processUpdates(_facesContext);
222 //            }
223 //        }, PhaseId.UPDATE_MODEL_VALUES, false, true, true);
224 //    }
225 //
226 //    /**
227 //     * Test method for {@link javax.faces.component.UIViewRoot#processApplication(javax.faces.context.FacesContext)}.
228 //     * 
229 //     * @throws Throwable
230 //     */
231 //    @Test
232 //    public void testProcessApplication() throws Throwable
233 //    {
234 //        testProcessXXX(new TestRunner()
235 //        {
236 //            public void run() throws Throwable
237 //            {
238 //                _testimpl.processApplication(_facesContext);
239 //            }
240 //        }, PhaseId.INVOKE_APPLICATION, false, true, true);
241 //    }
242 //
243 //    /**
244 //     * Test method for {@link javax.faces.component.UIViewRoot#encodeBegin(javax.faces.context.FacesContext)}.
245 //     * 
246 //     * @throws Throwable
247 //     */
248 //    @Test
249 //    public void testEncodeBegin() throws Throwable
250 //    {
251 //        testProcessXXX(new TestRunner()
252 //        {
253 //            public void run() throws Throwable
254 //            {
255 //                _testimpl.encodeBegin(_facesContext);
256 //            }
257 //        }, PhaseId.RENDER_RESPONSE, false, true, false);
258 //    }
259 //
260 //    /**
261 //     * Test method for {@link javax.faces.component.UIViewRoot#encodeEnd(javax.faces.context.FacesContext)}.
262 //     * 
263 //     * @throws Throwable
264 //     */
265 //    @Test
266 //    public void testEncodeEnd() throws Throwable
267 //    {
268 //        testProcessXXX(new TestRunner()
269 //        {
270 //            public void run() throws Throwable
271 //            {
272 //                _testimpl.encodeEnd(_facesContext);
273 //            }
274 //        }, PhaseId.RENDER_RESPONSE, false, false, true);
275 //    }
276 //
277 //    @Test
278 //    public void testEventQueue() throws Exception
279 //    {
280 //        FacesEvent event = _mocksControl.createMock(FacesEvent.class);
281 //        expect(event.getPhaseId()).andReturn(PhaseId.APPLY_REQUEST_VALUES).anyTimes();
282 //        UIComponent component = _mocksControl.createMock(UIComponent.class);
283 //        expect(event.getComponent()).andReturn(component).anyTimes();
284 //        component.broadcast(same(event));
285 //        _testimpl.queueEvent(event);
286 //
287 //        event = _mocksControl.createMock(FacesEvent.class);
288 //        expect(event.getPhaseId()).andReturn(PhaseId.PROCESS_VALIDATIONS).anyTimes();
289 //        _testimpl.queueEvent(event);
290 //
291 //        _mocksControl.replay();
292 //        _testimpl.processDecodes(_facesContext);
293 //        _mocksControl.verify();
294 //    }
295 //
296 //    @Test
297 //    public void testEventQueueWithAbortExcpetion() throws Exception
298 //    {
299 //        FacesEvent event = _mocksControl.createMock(FacesEvent.class);
300 //        expect(event.getPhaseId()).andReturn(PhaseId.INVOKE_APPLICATION).anyTimes();
301 //        UIComponent component = _mocksControl.createMock(UIComponent.class);
302 //        expect(event.getComponent()).andReturn(component).anyTimes();
303 //        component.broadcast(same(event));
304 //        expectLastCall().andThrow(new AbortProcessingException());
305 //        _testimpl.queueEvent(event);
306 //
307 //        event = _mocksControl.createMock(FacesEvent.class);
308 //        expect(event.getPhaseId()).andReturn(PhaseId.INVOKE_APPLICATION).anyTimes();
309 //        _testimpl.queueEvent(event);
310 //
311 //        _mocksControl.replay();
312 //        _testimpl.processApplication(_facesContext);
313 //        _mocksControl.verify();
314 //    }
315 
316     //
317     //
318     //
319     // /**
320     // * Test method for {@link javax.faces.component.UIViewRoot#saveState(javax.faces.context.FacesContext)}.
321     // */
322     // public void testSaveState()
323     // {
324     // fail("Not yet implemented"); // TODO
325     // }
326     //
327     // /**
328     // * Test method for
329     // * {@link javax.faces.component.UIViewRoot#restoreState(javax.faces.context.FacesContext, java.lang.Object)}.
330     // */
331     // public void testRestoreState()
332     // {
333     // fail("Not yet implemented"); // TODO
334     // }
335     //
336     // /**
337     // * Test method for {@link javax.faces.component.UIViewRoot#UIViewRoot()}.
338     // */
339     // public void testUIViewRoot()
340     // {
341     // fail("Not yet implemented"); // TODO
342     // }
343     //
344     //
345     // /**
346     // * Test method for {@link javax.faces.component.UIViewRoot#setLocale(java.util.Locale)}.
347     // */
348     // public void testSetLocale()
349     // {
350     // fail("Not yet implemented"); // TODO
351     // }
352     //
353     // /**
354     // * Test method for {@link javax.faces.component.UIViewRoot#getRenderKitId()}.
355     // */
356     // public void testGetRenderKitId()
357     // {
358     // fail("Not yet implemented"); // TODO
359     // }
360     //
361     // /**
362     // * Test method for {@link javax.faces.component.UIViewRoot#setRenderKitId(java.lang.String)}.
363     // */
364     // public void testSetRenderKitId()
365     // {
366     // fail("Not yet implemented"); // TODO
367     // }
368     //
369     // /**
370     // * Test method for {@link javax.faces.component.UIViewRoot#getViewId()}.
371     // */
372     // public void testGetViewId()
373     // {
374     // fail("Not yet implemented"); // TODO
375     // }
376     //
377     // /**
378     // * Test method for {@link javax.faces.component.UIViewRoot#setViewId(java.lang.String)}.
379     // */
380     // public void testSetViewId()
381     // {
382     // fail("Not yet implemented"); // TODO
383     // }
384     //
385     // /**
386     // * Test method for {@link javax.faces.component.UIViewRoot#addPhaseListener(javax.faces.event.PhaseListener)}.
387     // */
388     // public void testAddPhaseListener()
389     // {
390     // fail("Not yet implemented"); // TODO
391     // }
392     //
393     // /**
394     // * Test method for {@link javax.faces.component.UIViewRoot#removePhaseListener(javax.faces.event.PhaseListener)}.
395     // */
396     // public void testRemovePhaseListener()
397     // {
398     // fail("Not yet implemented"); // TODO
399     // }
400     //
401     // /**
402     // * Test method for {@link javax.faces.component.UIViewRoot#getBeforePhaseListener()}.
403     // */
404     // public void testGetBeforePhaseListener()
405     // {
406     // fail("Not yet implemented"); // TODO
407     // }
408     //
409     // /**
410     // * Test method for {@link javax.faces.component.UIViewRoot#setBeforePhaseListener(javax.el.MethodExpression)}.
411     // */
412     // public void testSetBeforePhaseListener()
413     // {
414     // fail("Not yet implemented"); // TODO
415     // }
416     //
417     // /**
418     // * Test method for {@link javax.faces.component.UIViewRoot#getAfterPhaseListener()}.
419     // */
420     // public void testGetAfterPhaseListener()
421     // {
422     // fail("Not yet implemented"); // TODO
423     // }
424     //
425     // /**
426     // * Test method for {@link javax.faces.component.UIViewRoot#setAfterPhaseListener(javax.el.MethodExpression)}.
427     // */
428     // public void testSetAfterPhaseListener()
429     // {
430     // fail("Not yet implemented"); // TODO
431     // }
432     //
433     // /**
434     // * Test method for {@link javax.faces.component.UIViewRoot#getFamily()}.
435     // */
436     // public void testGetFamily()
437     // {
438     // fail("Not yet implemented"); // TODO
439     // }
440     //
441 
442     private void testProcessXXX(TestRunner runner, PhaseId phaseId, boolean expectSuperCall, boolean checkBefore,
443             boolean checkAfter) throws Throwable
444     {
445         expect(_lifecycleFactory.getLifecycle(eq(LifecycleFactory.DEFAULT_LIFECYCLE))).andReturn(_lifecycle);
446         expect(_externalContext.getInitParameter(eq(FacesServlet.LIFECYCLE_ID_ATTR))).andReturn(null).anyTimes();
447 
448         PhaseEvent event = new PhaseEvent(_facesContext, phaseId, _lifecycle);
449 
450         if (expectSuperCall)
451         {
452             _testimpl = _mocksControl.createMock(UIViewRoot.class, new Method[]{UIViewRoot.class.getMethod(
453                     "isRendered", new Class[0])});
454         }
455 
456         MethodExpression beforeListener = _mocksControl.createMock(MethodExpression.class);
457         _testimpl.setBeforePhaseListener(beforeListener);
458 
459         MethodExpression afterListener = _mocksControl.createMock(MethodExpression.class);
460         _testimpl.setAfterPhaseListener(afterListener);
461 
462         Method[] mockedMethods = new Method[] {
463                 PhaseListener.class.getMethod("beforePhase", new Class[] { PhaseEvent.class }),
464                 PhaseListener.class.getMethod("afterPhase", new Class[] { PhaseEvent.class }) };
465         PhaseListener phaseListener = _mocksControl.createMock(phaseListenerClasses.get(phaseId), mockedMethods);
466         _testimpl.addPhaseListener(phaseListener);
467 
468         PhaseListener anyPhaseListener = _mocksControl.createMock(AnyPhasePhaseListener.class, mockedMethods);
469         _testimpl.addPhaseListener(anyPhaseListener);
470 
471         PhaseListener restoreViewPhaseListener = _mocksControl.createMock(RestoreViewPhasePhaseListener.class,
472                 mockedMethods);
473         _testimpl.addPhaseListener(restoreViewPhaseListener);
474 
475         _mocksControl.checkOrder(true);
476 
477         if (checkBefore)
478         {
479             expect(beforeListener.invoke(eq(_facesContext.getELContext()), aryEq(new Object[] { event }))).andReturn(
480                     null);
481             phaseListener.beforePhase(eq(event));
482             anyPhaseListener.beforePhase(eq(event));
483         }
484 
485         if (expectSuperCall)
486         {
487             expect(_testimpl.isRendered()).andReturn(false);
488         }
489 
490         if (checkAfter)
491         {
492             expect(afterListener.invoke(eq(_facesContext.getELContext()), aryEq(new Object[] { event }))).andReturn(
493                     null);
494             phaseListener.afterPhase(eq(event));
495             anyPhaseListener.afterPhase(eq(event));
496         }
497 
498         _mocksControl.replay();
499         runner.run();
500         _mocksControl.verify();
501     }
502 
503     private final class ActionListenerImplementation implements ActionListener
504     {
505         public int invocationCount = 0;
506         
507         public ActionEvent newActionEventFromListener;
508 
509         public ActionListenerImplementation(UICommand otherUiCommand)
510         {
511             // from spec: Queue one or more additional events, from the same source component
512             // or a DIFFERENT one
513             newActionEventFromListener = new ActionEvent(otherUiCommand);
514         }
515 
516         public void processAction(ActionEvent actionEvent)
517                 throws AbortProcessingException
518         {
519             invocationCount++;
520               
521             newActionEventFromListener.queue();
522             
523             // Simulate infinite recursion,most likely coding error:
524             actionEvent.queue();
525         }
526     }
527 
528     public static class MockLifeCycleFactory extends LifecycleFactory
529     {
530 
531         @Override
532         public void addLifecycle(String lifecycleId, Lifecycle lifecycle)
533         {
534             LIFECYCLEFACTORY.get().addLifecycle(lifecycleId, lifecycle);
535         }
536 
537         @Override
538         public Lifecycle getLifecycle(String lifecycleId)
539         {
540             return LIFECYCLEFACTORY.get().getLifecycle(lifecycleId);
541         }
542 
543         @Override
544         public Iterator<String> getLifecycleIds()
545         {
546             return LIFECYCLEFACTORY.get().getLifecycleIds();
547         }
548 
549     }
550 
551     public static abstract class ApplyRequesValuesPhaseListener implements PhaseListener
552     {
553         public PhaseId getPhaseId()
554         {
555             return PhaseId.APPLY_REQUEST_VALUES;
556         }
557     }
558 
559     public static abstract class ProcessValidationsPhaseListener implements PhaseListener
560     {
561         public PhaseId getPhaseId()
562         {
563             return PhaseId.PROCESS_VALIDATIONS;
564         }
565     }
566 
567     public static abstract class UpdateModelValuesPhaseListener implements PhaseListener
568     {
569         public PhaseId getPhaseId()
570         {
571             return PhaseId.UPDATE_MODEL_VALUES;
572         }
573     }
574 
575     public static abstract class InvokeApplicationPhaseListener implements PhaseListener
576     {
577         public PhaseId getPhaseId()
578         {
579             return PhaseId.INVOKE_APPLICATION;
580         }
581     }
582 
583     public static abstract class AnyPhasePhaseListener implements PhaseListener
584     {
585         public PhaseId getPhaseId()
586         {
587             return PhaseId.ANY_PHASE;
588         }
589     }
590 
591     public static abstract class RestoreViewPhasePhaseListener implements PhaseListener
592     {
593         public PhaseId getPhaseId()
594         {
595             return PhaseId.RESTORE_VIEW;
596         }
597     }
598 
599     public static abstract class RenderResponsePhaseListener implements PhaseListener
600     {
601         public PhaseId getPhaseId()
602         {
603             return PhaseId.RENDER_RESPONSE;
604         }
605     }
606 
607     @Test
608     public void testBroadcastEvents()
609     {
610         
611         UICommand uiCommand = new UICommand();
612         uiCommand.setId("idOfCommandOne");
613         facesContext.getViewRoot().getChildren().add(uiCommand);
614         
615         // Spec 3.4.2.6 Event Broadcasting: During event broadcasting, a listener processing an event may
616         // Queue one or more additional events from the same source component or a different one:
617         // and the DIFFERENT ONE is the next UICommand instance
618         UICommand differentUiCommand = new UICommand();
619         uiCommand.setId("idOfdifferentUiCommand");
620         facesContext.getViewRoot().getChildren().add(differentUiCommand);
621         
622         
623         ActionListenerImplementation actionListener = new ActionListenerImplementation(differentUiCommand);
624         uiCommand.addActionListener(actionListener);
625         
626         ActionListener differentActionListener = org.easymock.EasyMock.createNiceMock(ActionListener.class);
627         differentActionListener.processAction(actionListener.newActionEventFromListener);
628         org.easymock.EasyMock.expectLastCall().times(1);
629         org.easymock.EasyMock.replay(differentActionListener);
630         differentUiCommand.addActionListener(differentActionListener);
631         
632         // Simulates first event, in most cases click in GUI
633         ActionEvent invokeApplicationEvent = new ActionEvent(uiCommand);
634         invokeApplicationEvent.queue();
635         
636         // tested method: In this method is actionListener called and that
637         // listener itself queues new event
638         facesContext.getViewRoot().broadcastEvents(facesContext, PhaseId.INVOKE_APPLICATION);
639         
640         assertEquals(15, actionListener.invocationCount);
641         org.easymock.EasyMock.verify(differentActionListener);
642     }
643 
644 }