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