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: lu4242 $)
70   * @version $Revision: 1080409 $ $Date: 2011-03-10 19:33:29 -0500 (Thu, 10 Mar 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             _testimpl = _mocksControl.createMock(UIViewRoot.class, new Method[] { UIViewRoot.class.getMethod(
452                     "isRendered", new Class[0]) });
453 
454         MethodExpression beforeListener = _mocksControl.createMock(MethodExpression.class);
455         _testimpl.setBeforePhaseListener(beforeListener);
456 
457         MethodExpression afterListener = _mocksControl.createMock(MethodExpression.class);
458         _testimpl.setAfterPhaseListener(afterListener);
459 
460         Method[] mockedMethods = new Method[] {
461                 PhaseListener.class.getMethod("beforePhase", new Class[] { PhaseEvent.class }),
462                 PhaseListener.class.getMethod("afterPhase", new Class[] { PhaseEvent.class }) };
463         PhaseListener phaseListener = _mocksControl.createMock(phaseListenerClasses.get(phaseId), mockedMethods);
464         _testimpl.addPhaseListener(phaseListener);
465 
466         PhaseListener anyPhaseListener = _mocksControl.createMock(AnyPhasePhaseListener.class, mockedMethods);
467         _testimpl.addPhaseListener(anyPhaseListener);
468 
469         PhaseListener restoreViewPhaseListener = _mocksControl.createMock(RestoreViewPhasePhaseListener.class,
470                 mockedMethods);
471         _testimpl.addPhaseListener(restoreViewPhaseListener);
472 
473         _mocksControl.checkOrder(true);
474 
475         if (checkBefore)
476         {
477             expect(beforeListener.invoke(eq(_facesContext.getELContext()), aryEq(new Object[] { event }))).andReturn(
478                     null);
479             phaseListener.beforePhase(eq(event));
480             anyPhaseListener.beforePhase(eq(event));
481         }
482 
483         if (expectSuperCall)
484             expect(_testimpl.isRendered()).andReturn(false);
485 
486         if (checkAfter)
487         {
488             expect(afterListener.invoke(eq(_facesContext.getELContext()), aryEq(new Object[] { event }))).andReturn(
489                     null);
490             phaseListener.afterPhase(eq(event));
491             anyPhaseListener.afterPhase(eq(event));
492         }
493 
494         _mocksControl.replay();
495         runner.run();
496         _mocksControl.verify();
497     }
498 
499     private final class ActionListenerImplementation implements ActionListener
500     {
501         public int invocationCount = 0;
502         
503         public ActionEvent newActionEventFromListener;
504 
505         public ActionListenerImplementation(UICommand otherUiCommand)
506         {
507             // from spec: Queue one or more additional events, from the same source component
508             // or a DIFFERENT one
509             newActionEventFromListener = new ActionEvent(otherUiCommand);
510         }
511 
512         public void processAction(ActionEvent actionEvent)
513                 throws AbortProcessingException
514         {
515             invocationCount++;
516               
517             newActionEventFromListener.queue();
518             
519             // Simulate infinite recursion,most likely coding error:
520             actionEvent.queue();
521         }
522     }
523 
524     public static class MockLifeCycleFactory extends LifecycleFactory
525     {
526 
527         @Override
528         public void addLifecycle(String lifecycleId, Lifecycle lifecycle)
529         {
530             LIFECYCLEFACTORY.get().addLifecycle(lifecycleId, lifecycle);
531         }
532 
533         @Override
534         public Lifecycle getLifecycle(String lifecycleId)
535         {
536             return LIFECYCLEFACTORY.get().getLifecycle(lifecycleId);
537         }
538 
539         @Override
540         public Iterator<String> getLifecycleIds()
541         {
542             return LIFECYCLEFACTORY.get().getLifecycleIds();
543         }
544 
545     }
546 
547     public static abstract class ApplyRequesValuesPhaseListener implements PhaseListener
548     {
549         public PhaseId getPhaseId()
550         {
551             return PhaseId.APPLY_REQUEST_VALUES;
552         }
553     }
554 
555     public static abstract class ProcessValidationsPhaseListener implements PhaseListener
556     {
557         public PhaseId getPhaseId()
558         {
559             return PhaseId.PROCESS_VALIDATIONS;
560         }
561     }
562 
563     public static abstract class UpdateModelValuesPhaseListener implements PhaseListener
564     {
565         public PhaseId getPhaseId()
566         {
567             return PhaseId.UPDATE_MODEL_VALUES;
568         }
569     }
570 
571     public static abstract class InvokeApplicationPhaseListener implements PhaseListener
572     {
573         public PhaseId getPhaseId()
574         {
575             return PhaseId.INVOKE_APPLICATION;
576         }
577     }
578 
579     public static abstract class AnyPhasePhaseListener implements PhaseListener
580     {
581         public PhaseId getPhaseId()
582         {
583             return PhaseId.ANY_PHASE;
584         }
585     }
586 
587     public static abstract class RestoreViewPhasePhaseListener implements PhaseListener
588     {
589         public PhaseId getPhaseId()
590         {
591             return PhaseId.RESTORE_VIEW;
592         }
593     }
594 
595     public static abstract class RenderResponsePhaseListener implements PhaseListener
596     {
597         public PhaseId getPhaseId()
598         {
599             return PhaseId.RENDER_RESPONSE;
600         }
601     }
602 
603     @Test
604     public void testBroadcastEvents()
605     {
606         
607         UICommand uiCommand = new UICommand();
608         uiCommand.setId("idOfCommandOne");
609         facesContext.getViewRoot().getChildren().add(uiCommand);
610         
611         // Spec 3.4.2.6 Event Broadcasting: During event broadcasting, a listener processing an event may
612         // Queue one or more additional events from the same source component or a different one:
613         // and the DIFFERENT ONE is the next UICommand instance
614         UICommand differentUiCommand = new UICommand();
615         uiCommand.setId("idOfdifferentUiCommand");
616         facesContext.getViewRoot().getChildren().add(differentUiCommand);
617         
618         
619         ActionListenerImplementation actionListener = new ActionListenerImplementation(differentUiCommand);
620         uiCommand.addActionListener(actionListener);
621         
622         ActionListener differentActionListener = org.easymock.EasyMock.createNiceMock(ActionListener.class);
623         differentActionListener.processAction(actionListener.newActionEventFromListener);
624         org.easymock.EasyMock.expectLastCall().times(1);
625         org.easymock.EasyMock.replay(differentActionListener);
626         differentUiCommand.addActionListener(differentActionListener);
627         
628         // Simulates first event, in most cases click in GUI
629         ActionEvent invokeApplicationEvent = new ActionEvent(uiCommand);
630         invokeApplicationEvent.queue();
631         
632         // tested method: In this method is actionListener called and that
633         // listener itself queues new event
634         facesContext.getViewRoot().broadcastEvents(facesContext, PhaseId.INVOKE_APPLICATION);
635         
636         assertEquals(15, actionListener.invocationCount);
637         org.easymock.EasyMock.verify(differentActionListener);
638     }
639 
640 }