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 org.apache.myfaces.shared.context.flash;
20  
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Map;
26  
27  import javax.faces.application.FacesMessage;
28  import javax.faces.event.PhaseId;
29  import javax.servlet.ServletContext;
30  import javax.servlet.http.Cookie;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.apache.myfaces.test.base.AbstractViewControllerTestCase;
35  import org.apache.myfaces.test.mock.MockExternalContext20;
36  import org.apache.myfaces.test.mock.MockFacesContext20;
37  import org.apache.myfaces.test.mock.MockHttpServletRequest;
38  import org.apache.myfaces.test.mock.MockHttpServletResponse;
39  
40  /**
41   * Tests for FlashImpl.
42   * @author Jakob Korherr (latest modification by $Author: lu4242 $)
43   * @version $Revision: 1533536 $ $Date: 2013-10-18 11:20:12 -0500 (Fri, 18 Oct 2013) $
44   */
45  public class FlashImplTest extends AbstractViewControllerTestCase
46  {
47      
48      private FlashImpl _flash;
49  
50      public FlashImplTest(String name)
51      {
52          super(name);
53      }
54  
55      @Override
56      protected void setUpFacesContext() throws Exception
57      {
58          super.setUpFacesContext();
59  
60          // Unfortunately, setUpExternalContext() does not work, b/c MockFacesContext20 overwrites it!
61          externalContext = new MockExternalContext21(servletContext, request, response);
62          facesContext.setExternalContext(externalContext);
63      }
64  
65      @Override
66      protected void setUp() throws Exception
67      {
68          super.setUp();
69          
70          _flash = (FlashImpl) FlashImpl.getCurrentInstance(externalContext);
71      }
72  
73      @Override
74      protected void tearDown() throws Exception
75      {
76          _flash = null;
77          
78          super.tearDown();
79      }
80  
81      /**
82       * Tests if FlashImpl uses the sessionMap as base for the SubKeyMap
83       * and correctly stores the values in it.
84       * @throws Exception
85       */
86      public void testSessionMapWrapperSubKeyMap() throws Exception
87      {
88          // set phase to RESTORE_VIEW to create the flash tokens on doPrePhaseActions()
89          facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
90          _flash.doPrePhaseActions(facesContext);
91          
92          // put the value in the scope an keep() it!
93          _flash.putNow("testkey1", "testvalue1");
94          _flash.keep("testkey1");
95          
96          // set phase to RENDER_RESPONSE --> now renderMap will be used
97          facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
98          
99          // get the token for the render FlashMap (FlashImpl internals)
100         final String renderToken = (String) externalContext
101                 .getRequestMap().get(FlashImpl.FLASH_RENDER_MAP_TOKEN);
102         final String sessionMapKey = FlashImpl.FLASH_SESSION_MAP_SUBKEY_PREFIX + 
103                 FlashImpl.SEPARATOR_CHAR + renderToken + "testkey1";
104         
105         // Assertion
106         assertEquals("The render FlashMap must use the session Map to store the values.",
107                 "testvalue1", session.getAttribute(sessionMapKey));     
108     }
109     
110     /**
111      * Tests the functionality of keep() in a normal postback scenario.
112      * @throws Exception
113      */
114     @SuppressWarnings("unchecked")
115     public void testKeepValueNormalPostback() throws Exception
116     {
117         // simulate JSF lifecycle:
118         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
119         // and doPostPhaseActions() only on the last phase.
120         
121         // initial request ----------------------------------------------------
122         
123         // this request is a normal GET request, and thus not a postback
124         ((MockFacesContext20) facesContext).setPostback(false);
125         
126         // simulate JSF lifecycle
127         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
128         _flash.doPrePhaseActions(facesContext);
129         
130         // simulate JSF lifecycle
131         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
132         _flash.doPostPhaseActions(facesContext);
133         
134         // first postback -----------------------------------------------------
135         
136         // simulate a new request
137         _simulateNewRequest();
138         
139         // this request should be a postback
140         ((MockFacesContext20) facesContext).setPostback(true);
141         
142         // simulate JSF lifecycle
143         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
144         _flash.doPrePhaseActions(facesContext);
145         
146         // simulate JSF lifecycle
147         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
148         
149         // put the value in the scope an keep() it!
150         _flash.putNow("flashkey", "flashvalue");
151         _flash.keep("flashkey");
152         
153         // simulate JSF lifecycle
154         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
155         _flash.doPostPhaseActions(facesContext);
156         
157         // second postback ----------------------------------------------------
158         
159         // simulate a new request
160         _simulateNewRequest();
161         
162         // this request should be a postback
163         ((MockFacesContext20) facesContext).setPostback(true);
164         
165         // simulate JSF lifecycle
166         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
167         _flash.doPrePhaseActions(facesContext);
168         
169         // simulate JSF lifecycle
170         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
171         
172         // _flash.get() will ask the execute FlashMap for the value
173         // and this must be the render FlashMap of the previous request,
174         // thus it must contain the value from the previous request.
175         assertEquals("flashvalue", _flash.get("flashkey"));
176         
177         // simulate JSF lifecycle
178         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
179         _flash.doPostPhaseActions(facesContext);
180         
181         // _flash.get() also references to the execute FlashMap, but
182         // this one has to be cleared by now, thus it must be null.
183         assertNull("Execute FlashMap must have been cleared", _flash.get("flashkey"));
184         
185         // get the execute Map of the second postback (FlashImpl internals)
186         Map<String, Object> executeMap = (Map<String, Object>) externalContext
187                 .getRequestMap().get(FlashImpl.FLASH_EXECUTE_MAP);
188         
189         // must be empty
190         assertTrue("The execute Map of the second postback must have been cleared",
191                 executeMap.isEmpty());
192     }
193     
194     /**
195      * Tests the functionality of keep() in a POST-REDIRECT-GET scenario.
196      * @throws Exception
197      */
198     @SuppressWarnings("unchecked")
199     public void testKeepValuePostRedirectGet() throws Exception
200     {
201         // simulate JSF lifecycle:
202         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
203         // and doPostPhaseActions() only on the last phase.
204         
205         // initial request ----------------------------------------------------
206         
207         // this request is a normal GET request, and thus not a postback
208         ((MockFacesContext20) facesContext).setPostback(false);
209         
210         // simulate JSF lifecycle
211         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
212         _flash.doPrePhaseActions(facesContext);
213         
214         // simulate JSF lifecycle
215         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
216         _flash.doPostPhaseActions(facesContext);
217         
218         // first postback (POST of POST-REDIRECT-GET) -------------------------
219         
220         // simulate a new request
221         _simulateNewRequest();
222         
223         // this request should be a postback
224         ((MockFacesContext20) facesContext).setPostback(true);
225         
226         // simulate JSF lifecycle
227         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
228         _flash.doPrePhaseActions(facesContext);
229         
230         // simulate JSF lifecycle
231         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
232         
233         // put the value in the scope an keep() it!
234         _flash.put("flashkey", "flashvalue");
235         _flash.keep("flashkey");
236         
237         // set redirect to true, this happens by the NavigationHandler in phase 5
238         _flash.setRedirect(true);
239         
240         assertTrue("setRedirect(true) was just called, thus isRedirect() must be true",
241                 _flash.isRedirect());
242         
243         // note that setRedirect(true) was called, thus the cleanup happens
244         // in phase 5, because doPostPhaseActions() won't be called on phase 6.
245         _flash.doPostPhaseActions(facesContext);
246         
247         // GET request of POST-REDIRECT-GET -----------------------------------
248         
249         // simulate a new request
250         _simulateNewRequest();
251         
252         // this request is not a postback
253         ((MockFacesContext20) facesContext).setPostback(false);
254         
255         // simulate JSF lifecycle
256         // Note that doPrePhaseActions() is called on RESTORE_VIEW even
257         // though this request is not a postback.
258         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
259         _flash.doPrePhaseActions(facesContext);
260         
261         // check isRedirect();
262         assertTrue("setRedirect(true) was called on the previous request, "
263                 + " and we are in the execute portion of the lifecycle, "
264                 + " thus isRedirect() must be true.",
265                 _flash.isRedirect());
266         
267         // simulate JSF lifecycle - JSF will immediately jump to phase 6
268         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
269         
270         // check isRedirect();
271         assertFalse("setRedirect(true) was called on the previous request, "
272                 + " but we are already in the render portion of the lifecycle, "
273                 + " thus isRedirect() must be false.",
274                 _flash.isRedirect());
275         
276         // _flash.get() will ask the execute FlashMap and this one
277         // must contain the key used in keep()
278         assertEquals("flashvalue", _flash.get("flashkey"));
279         
280         _flash.doPostPhaseActions(facesContext);
281         
282         // second postback (after POST-REDIRECT-GET) --------------------------
283         
284         // simulate a new request
285         _simulateNewRequest();
286         
287         // this request should be a postback
288         ((MockFacesContext20) facesContext).setPostback(true);
289         
290         // simulate JSF lifecycle
291         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
292         _flash.doPrePhaseActions(facesContext);
293         
294         // simulate JSF lifecycle
295         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
296         
297         // check isRedirect();
298         assertFalse("setRedirect(true) was called on the pre-previous request, "
299                 + " thus isRedirect() must be false again.",
300                 _flash.isRedirect());
301         
302         // _flash.get() will ask the execute FlashMap for the value
303         // and this must be the render FlashMap of the previous (GET) request,
304         // thus it must not contain the value from the previous request,
305         // because the value was on the previous request's execute FlashMap
306         // and not on the previous request's render FlashMap.
307         assertNull(_flash.get("flashkey"));
308         
309         // simulate JSF lifecycle
310         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
311         _flash.doPostPhaseActions(facesContext);
312         
313         // get the execute Map of the second postback (FlashImpl internals)
314         Map<String, Object> executeMap = (Map<String, Object>) externalContext
315                 .getRequestMap().get(FlashImpl.FLASH_EXECUTE_MAP);
316         
317         // must be empty
318         assertTrue("The execute Map of the second postback must have been cleared",
319                 executeMap.isEmpty());
320     }
321     
322     /**
323      * Tests the functionality of keepMessages in a normal postback scenario.
324      * @throws Exception
325      */
326     public void testKeepMessagesNormalPostback() throws Exception
327     {
328         // simulate JSF lifecycle:
329         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
330         // and doPostPhaseActions() only on the last phase.
331         
332         // initial request ----------------------------------------------------
333         
334         // this request is a normal GET request, and thus not a postback
335         ((MockFacesContext20) facesContext).setPostback(false);
336         
337         // simulate JSF lifecycle
338         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
339         _flash.doPrePhaseActions(facesContext);
340         
341         // simulate JSF lifecycle
342         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
343         _flash.doPostPhaseActions(facesContext);
344         
345         // first postback -----------------------------------------------------
346         
347         // simulate a new request
348         _simulateNewRequest();
349         
350         // this request should be a postback
351         ((MockFacesContext20) facesContext).setPostback(true);
352         
353         // simulate JSF lifecycle
354         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
355         _flash.doPrePhaseActions(facesContext);
356         
357         // simulate JSF lifecycle
358         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
359         
360         // add FacesMessages to the facesContext
361         FacesMessage messageClientId = new FacesMessage("message for clientId");
362         facesContext.addMessage("clientId", messageClientId);
363         FacesMessage messageNoClientId = new FacesMessage("message without clientId");
364         facesContext.addMessage(null, messageNoClientId);
365         
366         // now the FacesContext must contain 2 messages
367         assertEquals(2, facesContext.getMessageList().size());
368         
369         // keep messages
370         _flash.setKeepMessages(true);
371         
372         assertTrue("setKeepMessages(true) was just called, thus isKeepMessages() "
373                 + "must be true.", _flash.isKeepMessages());
374         
375         // simulate JSF lifecycle
376         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
377         _flash.doPostPhaseActions(facesContext);
378         
379         // second postback ----------------------------------------------------
380         
381         // simulate a new request
382         _simulateNewRequest();
383         
384         // this request should be a postback
385         ((MockFacesContext20) facesContext).setPostback(true);
386         
387         // now the FacesContext must contain 0 messages (new request, new FacesContext)
388         assertEquals(0, facesContext.getMessageList().size());
389         
390         // simulate JSF lifecycle
391         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
392         _flash.doPrePhaseActions(facesContext);
393         
394         // now the messages must be here again
395         assertEquals(2, facesContext.getMessageList().size());
396         assertEquals(Arrays.asList(messageClientId), facesContext.getMessageList("clientId"));
397         assertEquals(Arrays.asList(messageNoClientId), facesContext.getMessageList(null));
398         
399         assertFalse("setKeepMessages(true) was not called on this request, thus "
400                 + "isKeepMessages() must be false.", _flash.isKeepMessages());
401         
402         // simulate JSF lifecycle
403         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
404         _flash.doPostPhaseActions(facesContext);
405         
406         // third postback ----------------------------------------------------
407         
408         // simulate a new request
409         _simulateNewRequest();
410         
411         // this request should be a postback
412         ((MockFacesContext20) facesContext).setPostback(true);
413         
414         // now the FacesContext must contain 0 messages (new request, new FacesContext)
415         assertEquals(0, facesContext.getMessageList().size());
416         
417         // simulate JSF lifecycle
418         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
419         _flash.doPrePhaseActions(facesContext);
420         
421         // the messages must still be gone here, because setKeepMessages(true)
422         // was not called on the previous request
423         assertEquals(0, facesContext.getMessageList().size());
424         
425         // simulate JSF lifecycle
426         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
427         _flash.doPostPhaseActions(facesContext); 
428     }
429     
430     /**
431      * Tests the functionality of keepMessages in a POST-REDIRECT-GET scenario.
432      * In this test case the messages are only shipped from the POST to the GET and
433      * then not from the GET to the next postback.
434      * @throws Exception
435      */
436     public void testKeepMessagesPostRedirectGet() throws Exception
437     {
438         // simulate JSF lifecycle:
439         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
440         // and doPostPhaseActions() only on the last phase.
441         
442         // initial request ----------------------------------------------------
443         
444         // this request is a normal GET request, and thus not a postback
445         ((MockFacesContext20) facesContext).setPostback(false);
446         
447         // simulate JSF lifecycle
448         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
449         _flash.doPrePhaseActions(facesContext);
450         
451         // simulate JSF lifecycle
452         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
453         _flash.doPostPhaseActions(facesContext);
454         
455         // first postback (POST of POST-REDIRECT-GET) -------------------------
456         
457         // simulate a new request
458         _simulateNewRequest();
459         
460         // this request should be a postback
461         ((MockFacesContext20) facesContext).setPostback(true);
462         
463         // simulate JSF lifecycle
464         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
465         _flash.doPrePhaseActions(facesContext);
466         
467         // simulate JSF lifecycle
468         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
469         
470         // add FacesMessages to the facesContext
471         FacesMessage messageClientId = new FacesMessage("message for clientId");
472         facesContext.addMessage("clientId", messageClientId);
473         FacesMessage messageNoClientId = new FacesMessage("message without clientId");
474         facesContext.addMessage(null, messageNoClientId);
475         
476         // now the FacesContext must contain 2 messages
477         assertEquals(2, facesContext.getMessageList().size());
478         
479         // keep messages
480         _flash.setKeepMessages(true);
481         assertTrue("setKeepMessages(true) was just called, thus isKeepMessages() "
482                 + "must be true.", _flash.isKeepMessages());
483         
484         // set redirect to true, this happens by the NavigationHandler in phase 5
485         _flash.setRedirect(true);
486         assertTrue("setRedirect(true) was just called, thus isRedirect() must be true",
487                 _flash.isRedirect());
488         // The redirect cause responseComplete() method to be called.
489         facesContext.responseComplete();
490         // note that setRedirect(true) was called, thus the cleanup happens
491         // in phase 5, because doPostPhaseActions() won't be called on phase 6.
492         _flash.doPostPhaseActions(facesContext);
493         
494         // GET request of POST-REDIRECT-GET -----------------------------------
495         
496         // simulate a new request
497         _simulateNewRequest();
498         
499         // this request is not a postback
500         ((MockFacesContext20) facesContext).setPostback(false);
501         
502         // now the FacesContext must contain 0 messages (new request, new FacesContext)
503         assertEquals(0, facesContext.getMessageList().size());
504         
505         // simulate JSF lifecycle
506         // Note that doPrePhaseActions() is called on RESTORE_VIEW even
507         // though this request is not a postback.
508         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
509         _flash.doPrePhaseActions(facesContext);
510         
511         // simulate JSF lifecycle - JSF will immediately jump to phase 6
512         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
513         
514         // now the messages must be here again
515         assertEquals(2, facesContext.getMessageList().size());
516         assertEquals(Arrays.asList(messageClientId), facesContext.getMessageList("clientId"));
517         assertEquals(Arrays.asList(messageNoClientId), facesContext.getMessageList(null));
518         
519         // check isKeepMessages()
520         assertFalse("setKeepMessages(true) was not called on this request, thus "
521                 + "isKeepMessages() must be false.", _flash.isKeepMessages());
522         
523         _flash.doPostPhaseActions(facesContext);
524         
525         // second postback (after POST-REDIRECT-GET) --------------------------
526         
527         // simulate a new request
528         _simulateNewRequest();
529         
530         // this request should be a postback
531         ((MockFacesContext20) facesContext).setPostback(true);
532         
533         // now the FacesContext must contain 0 messages (new request, new FacesContext)
534         assertEquals(0, facesContext.getMessageList().size());
535         
536         // simulate JSF lifecycle
537         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
538         _flash.doPrePhaseActions(facesContext);
539         
540         // simulate JSF lifecycle
541         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
542         
543         // now the FacesContext must contain 0 messages, because 
544         // setKeepMessages(true) was not called on the GET-request
545         assertEquals(0, facesContext.getMessageList().size());
546         
547         // simulate JSF lifecycle
548         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
549         _flash.doPostPhaseActions(facesContext);
550     }
551     
552     /**
553      * Tests the functionality of keepMessages in a POST-REDIRECT-GET scenario.
554      * In this test case the messages are shipped from the POST to the GET and
555      * then also from the GET to the next postback.
556      * @throws Exception
557      */
558     public void testKeepMessagesPostRedirectGetTwoTimes() throws Exception
559     {
560         // simulate JSF lifecycle:
561         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
562         // and doPostPhaseActions() only on the last phase.
563         
564         // initial request ----------------------------------------------------
565         
566         // this request is a normal GET request, and thus not a postback
567         ((MockFacesContext20) facesContext).setPostback(false);
568         
569         // simulate JSF lifecycle
570         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
571         _flash.doPrePhaseActions(facesContext);
572         
573         // simulate JSF lifecycle
574         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
575         _flash.doPostPhaseActions(facesContext);
576         
577         // first postback (POST of POST-REDIRECT-GET) -------------------------
578         
579         // simulate a new request
580         _simulateNewRequest();
581         
582         // this request should be a postback
583         ((MockFacesContext20) facesContext).setPostback(true);
584         
585         // simulate JSF lifecycle
586         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
587         _flash.doPrePhaseActions(facesContext);
588         
589         // simulate JSF lifecycle
590         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
591         
592         // add FacesMessages to the facesContext
593         FacesMessage messageClientId = new FacesMessage("message for clientId");
594         facesContext.addMessage("clientId", messageClientId);
595         FacesMessage messageNoClientId = new FacesMessage("message without clientId");
596         facesContext.addMessage(null, messageNoClientId);
597         
598         // now the FacesContext must contain 2 messages
599         assertEquals(2, facesContext.getMessageList().size());
600         
601         // keep messages
602         _flash.setKeepMessages(true);
603         assertTrue("setKeepMessages(true) was just called, thus isKeepMessages() "
604                 + "must be true.", _flash.isKeepMessages());
605         
606         // set redirect to true, this happens by the NavigationHandler in phase 5
607         _flash.setRedirect(true);
608         assertTrue("setRedirect(true) was just called, thus isRedirect() must be true",
609                 _flash.isRedirect());
610         // The redirect cause responseComplete() method to be called.
611         facesContext.responseComplete();
612         
613         // note that setRedirect(true) was called, thus the cleanup happens
614         // in phase 5, because doPostPhaseActions() won't be called on phase 6.
615         _flash.doPostPhaseActions(facesContext);
616         
617         // GET request of POST-REDIRECT-GET -----------------------------------
618         
619         // simulate a new request
620         _simulateNewRequest();
621         
622         // this request is not a postback
623         ((MockFacesContext20) facesContext).setPostback(false);
624         
625         // now the FacesContext must contain 0 messages (new request, new FacesContext)
626         assertEquals(0, facesContext.getMessageList().size());
627         
628         // simulate JSF lifecycle
629         // Note that doPrePhaseActions() is called on RESTORE_VIEW even
630         // though this request is not a postback.
631         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
632         _flash.doPrePhaseActions(facesContext);
633         
634         // simulate JSF lifecycle - JSF will immediately jump to phase 6
635         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
636         
637         // now the messages must be here again
638         assertEquals(2, facesContext.getMessageList().size());
639         assertEquals(Arrays.asList(messageClientId), facesContext.getMessageList("clientId"));
640         assertEquals(Arrays.asList(messageNoClientId), facesContext.getMessageList(null));
641         
642         // check isKeepMessages()
643         assertFalse("setKeepMessages(true) was not called on this request, thus "
644                 + "isKeepMessages() must be false.", _flash.isKeepMessages());
645         
646         // keep messages - again
647         _flash.setKeepMessages(true);
648         assertTrue("setKeepMessages(true) was just called, thus isKeepMessages() "
649                 + "must be true.", _flash.isKeepMessages());
650         // The redirect cause responseComplete() method to be called.
651         facesContext.responseComplete();
652         
653         _flash.doPostPhaseActions(facesContext);
654         
655         // second postback (after POST-REDIRECT-GET) --------------------------
656         
657         // simulate a new request
658         _simulateNewRequest();
659         
660         // this request should be a postback
661         ((MockFacesContext20) facesContext).setPostback(true);
662         
663         // now the FacesContext must contain 0 messages (new request, new FacesContext)
664         assertEquals(0, facesContext.getMessageList().size());
665         
666         // simulate JSF lifecycle
667         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
668         _flash.doPrePhaseActions(facesContext);
669         
670         // simulate JSF lifecycle
671         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
672         
673         // now the messages must be here again
674         assertEquals(2, facesContext.getMessageList().size());
675         assertEquals(Arrays.asList(messageClientId), facesContext.getMessageList("clientId"));
676         assertEquals(Arrays.asList(messageNoClientId), facesContext.getMessageList(null));
677         
678         // simulate JSF lifecycle
679         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
680         _flash.doPostPhaseActions(facesContext);
681         
682         // third postback -----------------------------------------------------
683         
684         // simulate a new request
685         _simulateNewRequest();
686         
687         // this request should be a postback
688         ((MockFacesContext20) facesContext).setPostback(true);
689         
690         // now the FacesContext must contain 0 messages (new request, new FacesContext)
691         assertEquals(0, facesContext.getMessageList().size());
692         
693         // simulate JSF lifecycle
694         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
695         _flash.doPrePhaseActions(facesContext);
696         
697         // simulate JSF lifecycle
698         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
699         
700         // now the FacesContext must contain 0 messages, because 
701         // setKeepMessages(true) was not called on the previous postback
702         assertEquals(0, facesContext.getMessageList().size());
703         
704         // simulate JSF lifecycle
705         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
706         _flash.doPostPhaseActions(facesContext);
707     }
708     
709     /**
710      * Test if setRedirect(true) works via _flash.put("redirect", true)
711      * and if isRedirect() is equal to _flash.get("redirect").
712      */
713     public void testSetRedirect()
714     {
715         assertFalse(_flash.isRedirect());
716         assertFalse((Boolean) _flash.get("redirect"));
717         
718         _flash.put("redirect", true);
719         
720         assertTrue(_flash.isRedirect());
721         assertTrue((Boolean) _flash.get("redirect"));
722     }
723     
724     /**
725      * Test if setKeepMessages(true) works via _flash.put("keepMessages", true)
726      * and if isKeepMessages() is equal to _flash.get("keepMessages").
727      */
728     public void testSetKeepMessages()
729     {
730         assertFalse(_flash.isKeepMessages());
731         assertFalse((Boolean) _flash.get("keepMessages"));
732         
733         _flash.put("keepMessages", true);
734         
735         assertTrue(_flash.isKeepMessages());
736         assertTrue((Boolean) _flash.get("keepMessages"));
737     }
738     
739     /**
740      * Tests the functionality of putNow().
741      */
742     @SuppressWarnings("unchecked")
743     public void testPutNow()
744     {
745         Map<String, Object> requestMap = externalContext.getRequestMap();
746         
747         // requestMap must NOT contain the key
748         assertNull(requestMap.get("flashkey"));
749         
750         _flash.putNow("flashkey", "flashvalue");
751         
752         // requestMap must contain the key
753         assertEquals("flashvalue", requestMap.get("flashkey"));
754     }
755     
756     /**
757      * Tests keep()
758      * @throws Exception
759      */
760     public void testKeep() throws Exception
761     {
762         // simulate JSF lifecycle:
763         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
764         // and doPostPhaseActions() only on the last phase.
765         
766         // initial request ----------------------------------------------------
767         
768         // this request is a normal GET request, and thus not a postback
769         ((MockFacesContext20) facesContext).setPostback(false);
770         
771         // simulate JSF lifecycle
772         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
773         _flash.doPrePhaseActions(facesContext);
774         
775         // simulate JSF lifecycle
776         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
777         _flash.doPostPhaseActions(facesContext);
778         
779         // first postback -----------------------------------------------------
780         
781         // simulate a new request
782         _simulateNewRequest();
783         
784         // this request should be a postback
785         ((MockFacesContext20) facesContext).setPostback(true);
786         
787         // simulate JSF lifecycle
788         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
789         _flash.doPrePhaseActions(facesContext);
790         
791         // simulate JSF lifecycle
792         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
793         
794         // put a value into the request FlashMap
795         _flash.putNow("flashkey", "flashvalue");
796         
797         // and keep() it
798         _flash.keep("flashkey");
799         
800         // simulate JSF lifecycle
801         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
802         
803         // cleanup flash
804         _flash.doPostPhaseActions(facesContext);
805         
806         // second postback ----------------------------------------------------
807         
808         // simulate a new request
809         _simulateNewRequest();
810         
811         // this request should be a postback
812         ((MockFacesContext20) facesContext).setPostback(true);
813         
814         // simulate JSF lifecycle
815         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
816         _flash.doPrePhaseActions(facesContext);
817         
818         // simulate JSF lifecycle
819         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
820      
821         // the value must be in the executeMap
822         assertEquals("flashvalue", _flash.get("flashkey"));
823         
824         // simulate JSF lifecycle
825         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
826         
827         // cleanup flash
828         _flash.doPostPhaseActions(facesContext);
829     }
830     
831     /**
832      * Like testKeep(), but without calling keep() to keep the value.
833      * @throws Exception
834      */
835     public void testNotKeep() throws Exception
836     {
837         // simulate JSF lifecycle:
838         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
839         // and doPostPhaseActions() only on RENDER_RESPONSE.
840         
841         // initial request ----------------------------------------------------
842         
843         // this request is a normal GET request, and thus not a postback
844         ((MockFacesContext20) facesContext).setPostback(false);
845         
846         // simulate JSF lifecycle
847         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
848         _flash.doPrePhaseActions(facesContext);
849         
850         // simulate JSF lifecycle
851         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
852         _flash.doPostPhaseActions(facesContext);
853         
854         // first postback -----------------------------------------------------
855         
856         // simulate a new request
857         _simulateNewRequest();
858         
859         // this request should be a postback
860         ((MockFacesContext20) facesContext).setPostback(true);
861         
862         // simulate JSF lifecycle
863         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
864         _flash.doPrePhaseActions(facesContext);
865         
866         // simulate JSF lifecycle
867         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
868         
869         // put a value into the request FlashMap
870         _flash.putNow("flashkey", "flashvalue");
871         
872         // and do NOT keep it.
873         
874         // simulate JSF lifecycle
875         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
876         
877         _flash.doPostPhaseActions(facesContext);
878         
879         // second postback ----------------------------------------------------
880         
881         // simulate a new request
882         _simulateNewRequest();
883         
884         // this request should be a postback
885         ((MockFacesContext20) facesContext).setPostback(true);
886         
887         // simulate JSF lifecycle
888         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
889         _flash.doPrePhaseActions(facesContext);
890         
891         // simulate JSF lifecycle
892         facesContext.setCurrentPhaseId(PhaseId.INVOKE_APPLICATION);
893      
894         // render FlashMap must be empty
895         assertNull(_flash.get("flashkey"));
896         
897         // simulate JSF lifecycle
898         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
899         
900         // cleanup flash
901         _flash.doPostPhaseActions(facesContext);
902     }
903     
904     /**
905      * Tests if the reading functions use _getFlashMapForReading()
906      * and if the writing functions use _getFlashMapForWriting().
907      */
908     public void testMapMethodsUseDifferentMaps() throws Exception
909     {
910         // simulate JSF lifecycle:
911         // note that doPrePhaseActions() only performs tasks on RESTORE_VIEW
912         // and doPostPhaseActions() only on RENDER_RESPONSE.
913         
914         // initial request ----------------------------------------------------
915         
916         // this request is a normal GET request, and thus not a postback
917         ((MockFacesContext20) facesContext).setPostback(false);
918         
919         // simulate JSF lifecycle
920         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
921         _flash.doPrePhaseActions(facesContext);
922         
923         // simulate JSF lifecycle
924         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
925         _flash.doPostPhaseActions(facesContext);
926         
927         // first postback -----------------------------------------------------
928         
929         // simulate a new request
930         _simulateNewRequest();
931         
932         // this request should be a postback
933         ((MockFacesContext20) facesContext).setPostback(true);
934         
935         // simulate JSF lifecycle
936         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
937         _flash.doPrePhaseActions(facesContext);
938         
939         // simulate JSF lifecycle
940         facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
941         
942         // in this configuration put() and get() are executed on different maps
943         
944         // there must not be a value with the key "flashkey"
945         assertNull(_flash.get("flashkey"));
946         
947         // put() always references the active FlashMap,
948         // which is the render FlashMap in this case (phase is render response)
949         _flash.put("flashkey", "flashvalue");
950         
951         // there must still not be a value with the key "flashkey"
952         // NOTE that get still references the execute FlashMap
953         assertNull(_flash.get("flashkey"));
954         
955         _flash.doPostPhaseActions(facesContext);
956     }
957     
958     /**
959      * Tests the implementation of the methods from the java.util.Map interface.
960      */
961     public void testMapMethods()
962     {
963         // ensure that _getActiveFlashMap() returns the execute FlashMap
964         facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
965         
966         // run assertions for an empty FlashMap
967         _noElementAssertions();
968         
969         // use put() to put a value into the map
970         _flash.put("flashkey", "flashvalue");
971         
972         // run assertions for the FlashMap with one element
973         _oneElementAssertions();
974         
975         // remove the key using remove();
976         _flash.remove("flashkey");
977         
978         _noElementAssertions();
979 
980         // use putAll() to put a value into the map
981         Map<String, Object> map = new HashMap<String, Object>();
982         map.put("flashkey", "flashvalue");
983         _flash.putAll(map);
984         
985         _oneElementAssertions();
986         
987         // use clear() to remove the value from the map
988         _flash.clear();
989         
990         _noElementAssertions();
991         
992         // put the value into the map again
993         _flash.put("flashkey", "flashvalue");
994         
995         _oneElementAssertions();
996         
997         // use the keySet to clear the map
998         _flash.keySet().clear();
999         
1000         _noElementAssertions();
1001     }
1002     
1003     /**
1004      * Utility method used by testMapMethods()
1005      */
1006     private void _noElementAssertions()
1007     {
1008         assertTrue(_flash.isEmpty());
1009         assertEquals(0, _flash.size());
1010         assertFalse(_flash.containsKey("flashkey"));
1011         assertFalse(_flash.containsValue("flashvalue"));
1012         assertEquals(Collections.emptySet(), _flash.keySet());
1013         assertNull(_flash.get("flashkey"));
1014         assertTrue(_flash.values().isEmpty());
1015     }
1016     
1017     /**
1018      * Utility method used by testMapMethods()
1019      */
1020     private void _oneElementAssertions()
1021     {
1022         assertFalse(_flash.isEmpty());
1023         assertEquals(1, _flash.size());
1024         assertTrue(_flash.containsKey("flashkey"));
1025         assertTrue(_flash.containsValue("flashvalue"));
1026         assertEquals(new HashSet<String>(Arrays.asList("flashkey")), _flash.keySet());
1027         assertEquals("flashvalue", _flash.get("flashkey"));
1028         assertTrue(_flash.values().contains("flashvalue"));
1029     }
1030     
1031     /**
1032      * Create new request, response, ExternalContext and FacesContext
1033      * to simulate a new request. Also resend any Cookies added to the
1034      * current request by the Flash implementation.
1035      * 
1036      * @throws Exception
1037      */
1038     private void _simulateNewRequest() throws Exception
1039     {
1040         // we will now have a cookie with the token for the new request
1041         Cookie renderTokenCookie = response.getCookie(FlashImpl.FLASH_RENDER_MAP_TOKEN);
1042         
1043         // the Cookie must exist
1044         assertNotNull(renderTokenCookie);
1045         
1046         // check for the redirect-cookie
1047         Cookie redirectCookie = response.getCookie(FlashImpl.FLASH_REDIRECT);
1048         
1049         // create new request, response, ExternalContext and FacesContext
1050         // to simulate a new request
1051         request = new MockHttpServletRequest(session);
1052         request.setServletContext(servletContext);
1053         response = new MockHttpServletResponse();
1054         setUpExternalContext();
1055         setUpFacesContext();
1056         
1057         // add the cookie to the new request
1058         request.addCookie(renderTokenCookie);
1059         
1060         // add the redirect-cookie to the new request, if exists
1061         if (redirectCookie != null)
1062         {
1063             // maxage == 0 means remove the cookie
1064             if (redirectCookie.getMaxAge() != 0)
1065             {
1066                 request.addCookie(redirectCookie);
1067             }
1068         }
1069     }
1070 
1071     /**
1072      * Adds isSecure() implementation to MockExternalContext20.
1073      *
1074      * TODO remove this one as soon as MyFaces-Test provides MockExternalContext21.
1075      */
1076     private static class MockExternalContext21 extends MockExternalContext20
1077     {
1078 
1079         private boolean secure = false;
1080 
1081         private MockExternalContext21(ServletContext context, HttpServletRequest request, HttpServletResponse response)
1082         {
1083             super(context, request, response);
1084         }
1085 
1086         @Override
1087         public boolean isSecure()
1088         {
1089             return secure;
1090         }
1091 
1092         public void setSecure(boolean secure)
1093         {
1094             this.secure = secure;
1095         }
1096     }
1097 
1098 }