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 }